Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc339e5246 | ||
|
|
f5410bdc4c | ||
|
|
f801b2fbfd | ||
|
|
5241597594 | ||
|
|
a5f982de8d | ||
|
|
ea24207b57 | ||
|
|
6b7d429491 | ||
|
|
5891bfb302 | ||
|
|
d672cc2399 | ||
|
|
25bea07b42 | ||
|
|
bd8d73ab1d | ||
|
|
0cfef92ae1 | ||
|
|
b81431d7ff | ||
|
|
4a7a4b5744 | ||
|
|
4dfe3464b2 | ||
|
|
c1e04bf8c0 | ||
|
|
a00e4ac363 | ||
|
|
d36cb61210 | ||
|
|
0ad42752d3 | ||
|
|
dcdab56acb | ||
|
|
5af8fe702b | ||
|
|
7d8e4e4338 | ||
|
|
7c6c25dda7 | ||
|
|
89b1941c8e | ||
|
|
71b696582b | ||
|
|
43ac3d1857 | ||
|
|
fa8b87c470 | ||
|
|
afa5da745e | ||
|
|
22d5a40a2e | ||
|
|
264862bf2e | ||
|
|
044f180222 | ||
|
|
3b67b362aa | ||
|
|
f1126ff7bf | ||
|
|
3a0e91dd91 | ||
|
|
90062c0922 | ||
|
|
70835d3d5f | ||
|
|
ccfaf520b7 | ||
|
|
bbbd208743 | ||
|
|
c19c2d6723 | ||
|
|
08f4ba99ce | ||
|
|
df8b4d8a5a | ||
|
|
00266efe13 | ||
|
|
335c042d16 | ||
|
|
d0067ba34a | ||
|
|
9258df02cc | ||
|
|
1502cfbc8c | ||
|
|
84c89d1107 | ||
|
|
0bd14ca16c | ||
|
|
9dee989a79 | ||
|
|
f789fd1fd8 | ||
|
|
ccc7da309d | ||
|
|
56679dd2ae | ||
|
|
52c84f6f58 | ||
|
|
4852c830af | ||
|
|
949966712e | ||
|
|
4d7de00d4c | ||
|
|
b231c28cf6 | ||
|
|
7dd6941393 | ||
|
|
1bb2fa23de | ||
|
|
ab58edcdf5 | ||
|
|
60956c8c56 | ||
|
|
5518363aa6 | ||
|
|
4108d1363a | ||
|
|
0bc2b42001 | ||
|
|
c38f96fa69 | ||
|
|
c4b6aa3061 | ||
|
|
dceefa22cb | ||
|
|
6a6adf703f | ||
|
|
de4579e993 | ||
|
|
6b101ff23b | ||
|
|
6389e50337 | ||
|
|
d23be5c089 | ||
|
|
c0cbbf8d50 | ||
|
|
a7d13893a6 | ||
|
|
1bfa60ac20 | ||
|
|
9d8e780332 | ||
|
|
40428da1dd | ||
|
|
ef59174c1e | ||
|
|
53e0943223 | ||
|
|
b718ad166d | ||
|
|
690fc51854 | ||
|
|
23196daa11 | ||
|
|
0add6a5aae | ||
|
|
0dbc046ca6 | ||
|
|
2a0410f171 | ||
|
|
4bfdadb7de | ||
|
|
1629266e3c | ||
|
|
b3aadaf474 | ||
|
|
eb7acc70bf | ||
|
|
998882045b | ||
|
|
eb41f358cf | ||
|
|
17f581ff75 | ||
|
|
edbd291a28 | ||
|
|
2b263a7fe9 | ||
|
|
d98fc3859d | ||
|
|
d0eca0517e | ||
|
|
85b379c4ca | ||
|
|
22c84ce64a | ||
|
|
dd0015aaf1 | ||
|
|
7d6f90485e | ||
|
|
04ae07ab8c | ||
|
|
98f4072f1f | ||
|
|
5b676ba9f3 | ||
|
|
14cd78c322 | ||
|
|
8716b52411 | ||
|
|
a35623707e | ||
|
|
2a6fc712eb | ||
|
|
506400748e | ||
|
|
57d64b6ba5 | ||
|
|
ff78165978 | ||
|
|
c3d7d0aafc | ||
|
|
90e433df06 | ||
|
|
0ba4d6f43a | ||
|
|
080c0e2b2b | ||
|
|
9eec57b469 | ||
|
|
8d198d6ec7 | ||
|
|
c2858d2aa0 | ||
|
|
e52a71602d | ||
|
|
8443692e2b |
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: "43"
|
||||
version: "44"
|
||||
---
|
||||
|
||||
# REA
|
||||
@@ -30,6 +30,8 @@ unavailable, read [connection and recovery](references/connection-and-recovery.m
|
||||
Diagnose the observed blocker, continue useful supported work, and resume the
|
||||
original task after repair. Client approval, shell PATH, registration and engine
|
||||
availability have different remedies; avoid repeating setup for all of them.
|
||||
If the host times out without a REA result, do not repeat the unchanged call or
|
||||
assume work stopped. Read the recovery guide's deadline distinction before retrying.
|
||||
|
||||
1. Diagnose without changing REA or client configuration. `npx` may download
|
||||
the package and write to npm's cache. For Codex, run
|
||||
@@ -89,9 +91,19 @@ explicit path or endpoint and do not need it.
|
||||
|
||||
- ASAR or extracted JavaScript/Electron tree:
|
||||
`analyze_javascript_application`.
|
||||
- Archive/package member inventory (ZIP/APK/IPA/MSIX/AppX or DMG):
|
||||
`open_binary` with the supplied local path; use `inspect_artifact` when its
|
||||
graph and findings help answer the question.
|
||||
- Archive/package member inventory (ZIP/APK/IPA/MSIX/AppX, DMG, or a PyInstaller
|
||||
executable): `open_binary` with the supplied local path; use `inspect_artifact`
|
||||
when its graph and findings help answer the question. A PyInstaller CArchive
|
||||
is the application layer; the bootloader is only the native prefix. Start with
|
||||
`detail: "summary"` for large inventories, then use retained inventory views
|
||||
for selected member fields/pages. A complete small inventory can cost less
|
||||
than extra calls and schemas. Runtime options are metadata, not files.
|
||||
Compact delivery still inventories and hashes members. See the
|
||||
[Python packaged applications guide](https://github.com/morluto/rea/blob/main/docs/python-packaged-applications.md).
|
||||
- Apple app plist, Interface Builder, asset catalog, or keyed archive resources:
|
||||
`open_bundle_resources`, then the selected resource tool. This does not
|
||||
require `CFBundleExecutable` or a deep provider. Resource sessions have no
|
||||
executable digest or binary snapshot; use `open_binary` for native code.
|
||||
- Android APK code, classes, methods, or incoming references:
|
||||
`inspect_android_package`, then focused Android tools when advertised.
|
||||
Archive member inventory still uses the archive route above.
|
||||
@@ -169,6 +181,15 @@ 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).
|
||||
- Bundled GGUF or safetensors model declarations: `inspect_model_artifact`
|
||||
with the explicit local file. Default header hashing leaves full-file identity
|
||||
unknown; `hash: "full"` additionally reads all weights for SHA-256. MCP returns
|
||||
retained summaries; optional `view` selects one page in the initial call. Use
|
||||
`inspect_analysis_view` kind `model-artifact` for
|
||||
batched metadata keys, tensor names or type totals, selected fields and pages.
|
||||
Preserve header-scoped indices and array preview omissions; do not infer model
|
||||
behavior from declared architecture or tensor types. See the
|
||||
[model file guide](https://github.com/morluto/rea/blob/main/docs/model-artifacts.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,
|
||||
@@ -186,6 +207,12 @@ explicit path or endpoint and do not need it.
|
||||
package order, dependency indices, and the section file range. A missing
|
||||
`.dep-v0` section is null metadata, not proof the binary has no dependencies.
|
||||
A rustc commit hash is an inference from a read-only `/rustc/<40-hex>/` path.
|
||||
For selected crates, use `view: {collection: "packages", names: [...],
|
||||
fields: ["name", "version", "source"]}` on the first call; explicitly include
|
||||
`dependencies` for direct index references. Use `detail: "summary"` for counts
|
||||
and ranges, or retained `inspect_analysis_view` Rust package/edge pages for
|
||||
subsequent filters. Keep the complete parent Evidence accessible; CLI compact
|
||||
delivery requires `--evidence_output`.
|
||||
See the
|
||||
[Rust binary guide](https://github.com/morluto/rea/blob/main/docs/rust-binaries.md).
|
||||
|
||||
|
||||
@@ -34,6 +34,47 @@ answers part of the question, and name the unavailable evidence separately.
|
||||
Do not substitute another target. Label observations from ordinary shell tools
|
||||
as such; only actual REA output supplies REA Evidence.
|
||||
|
||||
## Distinguish client and server deadlines
|
||||
|
||||
A host message such as `timed out awaiting tools/call` with no REA result means
|
||||
the client's deadline expired before a result arrived. It does not establish
|
||||
that REA finished, canceled successfully, or released all resources. Do not
|
||||
repeat the unchanged analysis. Inspect available session/provider state and
|
||||
retained Evidence only when useful; an idle `binary_session` is not proof that
|
||||
every target-free operation stopped. Never invent a missing Evidence reference.
|
||||
|
||||
Allow the host tool deadline to cover execution, cancellation/cleanup, and
|
||||
response delivery. In Codex, the setting is
|
||||
`mcp_servers.<server-id>.tool_timeout_sec` in **seconds**, not
|
||||
`startup_timeout_sec`. Select a value for the actual operation budget and
|
||||
headroom, not a universal timeout. See the
|
||||
[official MCP configuration reference](https://learn.chatgpt.com/docs/extend/mcp?surface=cli).
|
||||
Apply configuration changes only within the authorized scope, preserving
|
||||
unrelated entries and backups; reconnect/restart as required by the host.
|
||||
When configuration is not editable, report the setting needed or use an
|
||||
authorized CLI fallback. Do not silently broaden permissions or switch targets.
|
||||
|
||||
A returned REA `provider_timeout` instead means the **server/provider** reached
|
||||
its deadline. Follow its `remediation.action`, reuse accessible partial Evidence,
|
||||
and resolve reported incomplete cleanup before retrying. Raising only the
|
||||
client deadline does not change this server budget. For static JavaScript,
|
||||
`analysis_timeout_ms` (CLI `rea analyze-javascript-application <path>
|
||||
--analysis-timeout-ms <milliseconds>`) controls each source's
|
||||
startup, analysis and transfer deadline in **milliseconds**. Increase that
|
||||
budget or reduce semantic work / select exact inventoried `source_paths` when
|
||||
the task permits. Omit `source_paths` for all sources; `[]` is invalid.
|
||||
`detail: "summary"` reduces delivery, not analysis work. A per-source budget
|
||||
does not bound the elapsed time for an entire multi-source application.
|
||||
Partial-Evidence inspection can avoid recomputation; an unavailable reader is
|
||||
not itself a reason to block a corrected static-analysis retry.
|
||||
The shorthand `rea analyze <path>` does not expose this timeout flag; use the
|
||||
explicit JavaScript command when changing that budget.
|
||||
|
||||
For invalid arguments, use the connected tool's current input schema, or the
|
||||
installed CLI's command help. Refresh the catalog if stale; do not guess fields
|
||||
from an older skill or return the entire catalog to repair one field. Correct
|
||||
all reported fields before retrying.
|
||||
|
||||
## Repair registration when needed
|
||||
|
||||
For Codex:
|
||||
|
||||
@@ -18,7 +18,8 @@ only selected files. The result and summary record `source_selection`; files
|
||||
outside scope and their relationships remain unknown, so partial application
|
||||
coverage does not mean the selected source failed. Use repeated `--source-path`
|
||||
on either static application CLI route. Omit the selection when the question
|
||||
requires complete application analysis. `detail: "summary"` reduces delivery
|
||||
requires complete application analysis; `source_paths: []` is invalid, not an
|
||||
unfiltered request. `detail: "summary"` reduces delivery
|
||||
and does not by itself reduce analysis work.
|
||||
|
||||
Exact `#alias` specifiers resolve through the importer's
|
||||
@@ -35,7 +36,9 @@ the complete application Evidence inline when it fits.
|
||||
When the connected schema advertises `max_heap_mb` and `analysis_timeout_ms`,
|
||||
these select the isolated source analyzer's old-space heap and per-source
|
||||
deadline. Defaults are 1024 MiB and 300000 ms. They do not change the MCP
|
||||
process heap. A Bun-hosted REA does not enforce the heap choice; on Windows it
|
||||
process heap or the client's tool deadline. For a host timeout without REA
|
||||
output, use the [deadline recovery guide](connection-and-recovery.md#distinguish-client-and-server-deadlines)
|
||||
instead of raising the server budget. A Bun-hosted REA does not enforce the heap choice; on Windows it
|
||||
instead limits the analyzer's committed memory to that choice plus a 512 MiB
|
||||
runtime reserve.
|
||||
Resource-limited analyses preserve completed static facts and
|
||||
|
||||
@@ -11,6 +11,22 @@ 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.
|
||||
|
||||
## Apple app resources
|
||||
|
||||
Use `open_bundle_resources` with the selected app path, then `inspect_plist`,
|
||||
`decode_interface_builder`, `inspect_asset_catalog`, or `inspect_keyed_archive`.
|
||||
The resource session reads the app without resolving `CFBundleExecutable` or
|
||||
starting a deep provider. Preserve `resource_root` provenance, per-resource
|
||||
hashes where reported, and null executable identity; a directory path is not
|
||||
an executable digest. Export retained Evidence before closing or switching
|
||||
roots. Binary analysis snapshots do not apply. Use `open_binary` to switch to
|
||||
executable analysis, and `close_binary` to release either session kind.
|
||||
|
||||
The four matching CLI commands accept the app path directly. For an archive
|
||||
inside the app, use `inspect-keyed-archive APP_PATH RELATIVE_ARCHIVE_PATH`.
|
||||
Host requirements for plist and compiled asset/Interface Builder tooling still
|
||||
apply; a resource-only session does not establish provider availability.
|
||||
|
||||
## Desktop installation directories
|
||||
|
||||
When an installation directory is supplied and its runtime is unknown, use
|
||||
@@ -38,6 +54,16 @@ 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.
|
||||
|
||||
## Model files
|
||||
|
||||
Use `inspect_model_artifact` (`inspect-model-artifact PATH`) for local GGUF v2/v3
|
||||
and safetensors declarations without a model runtime. Header-only mode does not
|
||||
read weights or establish full-file identity. `hash: "full"` explicitly streams
|
||||
all bytes. Retained `model-artifact` views page `metadata`, `tensors` and derived
|
||||
`types`, with exact batch filters and selected fields. Arrays retain typed counts
|
||||
and eight-element previews; omitted values, unknown types and runtime behavior
|
||||
stay explicit. See [model files](https://github.com/morluto/rea/blob/main/docs/model-artifacts.md).
|
||||
|
||||
## IL2CPP metadata
|
||||
|
||||
Use `inspect_il2cpp_metadata` for an explicit local Unity `global-metadata.dat`
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
name: Cache npm package downloads
|
||||
description: Restore npm's download cache and save it only from trusted default-branch events.
|
||||
description: Restore npm's download cache. Trusted default-branch jobs that opt in may save it.
|
||||
inputs:
|
||||
save:
|
||||
description: Save the shared download cache when this event is allowed to write it.
|
||||
required: false
|
||||
default: "false"
|
||||
outputs:
|
||||
can-save:
|
||||
description: Whether this run may update the shared npm download cache.
|
||||
@@ -23,20 +28,25 @@ runs:
|
||||
can_save=true
|
||||
fi
|
||||
cache_key="npm-download-${RUNNER_OS}-${runtime_arch}-${LOCKFILES_HASH}"
|
||||
# Same platform only. A lockfile change can reuse tarballs from the previous key.
|
||||
restore_key="npm-download-${RUNNER_OS}-${runtime_arch}-"
|
||||
{
|
||||
printf 'can-save=%s\n' "${can_save}"
|
||||
printf 'path=%s\n' "${cache_path}"
|
||||
printf 'key=%s\n' "${cache_key}"
|
||||
printf 'restore-key=%s\n' "${restore_key}"
|
||||
} >> "${GITHUB_OUTPUT}"
|
||||
- name: Restore and save npm package downloads
|
||||
if: steps.context.outputs.can-save == 'true'
|
||||
if: steps.context.outputs.can-save == 'true' && inputs.save == 'true'
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: ${{ steps.context.outputs.path }}
|
||||
key: ${{ steps.context.outputs.key }}
|
||||
restore-keys: ${{ steps.context.outputs.restore-key }}
|
||||
- name: Restore npm package downloads without saving
|
||||
if: steps.context.outputs.can-save != 'true'
|
||||
if: steps.context.outputs.can-save != 'true' || inputs.save != 'true'
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: ${{ steps.context.outputs.path }}
|
||||
key: ${{ steps.context.outputs.key }}
|
||||
restore-keys: ${{ steps.context.outputs.restore-key }}
|
||||
@@ -0,0 +1,64 @@
|
||||
name: Cache retention
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["CI"]
|
||||
types: [completed]
|
||||
|
||||
# Qualification calls ci.yml with actions: read. A nested workflow cannot raise
|
||||
# that token, so cache deletion stays in this workflow.
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write
|
||||
|
||||
concurrency:
|
||||
group: cache-retention-${{ github.event.workflow_run.head_sha }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
retain:
|
||||
name: Retain Turbo snapshots and npm download caches
|
||||
# Restrict the triggering-source checkout to same-repository default-branch
|
||||
# CI events. The helper runs from this trusted workflow revision; triggering
|
||||
# source is used only for cache identity and is never executed. Static and
|
||||
# build save snapshots before later lanes finish, so failed, timed-out, or
|
||||
# cancelled runs still retain those snapshots.
|
||||
if: >-
|
||||
(github.event.workflow_run.conclusion == 'success' ||
|
||||
github.event.workflow_run.conclusion == 'failure' ||
|
||||
github.event.workflow_run.conclusion == 'timed_out' ||
|
||||
github.event.workflow_run.conclusion == 'cancelled') &&
|
||||
github.event.workflow_run.head_repository.full_name == github.repository &&
|
||||
github.event.workflow_run.head_branch == github.event.repository.default_branch &&
|
||||
(github.event.workflow_run.event == 'push' ||
|
||||
github.event.workflow_run.event == 'schedule' ||
|
||||
github.event.workflow_run.event == 'workflow_dispatch')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
with:
|
||||
ref: ${{ github.workflow_sha }}
|
||||
path: trusted-retention
|
||||
persist-credentials: false
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
with:
|
||||
ref: ${{ github.event.workflow_run.head_sha }}
|
||||
path: triggering-source
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: trusted-retention/.nvmrc
|
||||
- name: Retain current Turbo snapshots and npm download caches
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
CI_REPOSITORY: ${{ github.repository }}
|
||||
CACHE_PREFIX: rea-static-v1-${{ runner.os }}-${{ runner.arch }}-
|
||||
CACHE_KEEP_KEY: rea-static-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('triggering-source/.nvmrc', 'triggering-source/.npmrc', 'triggering-source/package-lock.json') }}-${{ github.event.workflow_run.head_sha }}
|
||||
CACHE_DEEP_PREFIX: rea-static-deep-v1-${{ runner.os }}-${{ runner.arch }}-
|
||||
CACHE_DEEP_KEEP_KEY: rea-static-deep-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('triggering-source/.nvmrc', 'triggering-source/.npmrc', 'triggering-source/package-lock.json') }}-${{ github.event.workflow_run.head_sha }}
|
||||
CACHE_KNIP_PREFIX: knip-meta-v1-${{ runner.os }}-
|
||||
CACHE_KNIP_KEEP_KEY: knip-meta-v1-${{ runner.os }}-${{ hashFiles('triggering-source/knip.json', 'triggering-source/tsconfig.json', 'triggering-source/package.json', 'triggering-source/package-lock.json') }}-${{ github.event.workflow_run.head_sha }}
|
||||
CACHE_PORTABLE_PREFIX: rea-portable-build-v2-${{ runner.os }}-${{ runner.arch }}-
|
||||
DEDUPE_NPM_DOWNLOADS: "true"
|
||||
run: node trusted-retention/scripts/ci/cache-retention.mjs
|
||||
+149
-15
@@ -15,6 +15,15 @@ on:
|
||||
description: Exact qualification source; must match the caller revision.
|
||||
required: true
|
||||
type: string
|
||||
remote_cache_write:
|
||||
description: Trusted release qualification may publish the remote cache.
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
TURBO_TOKEN:
|
||||
description: Remote cache token. Only trusted release qualification passes it.
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -29,6 +38,15 @@ concurrency:
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# Set Actions secret TURBO_TOKEN and variable TURBO_TEAM. OIDC can mint
|
||||
# TURBO_TOKEN via vercel/setup-turborepo-remote-cache-action; do not commit a token.
|
||||
# A called workflow sees the caller event, so workflow_call is not an event
|
||||
# name here. Release qualification sets remote_cache_write and passes the
|
||||
# token. Open pull requests get an empty token and the local cache only.
|
||||
# Remote reads need a credential, and pull requests do not receive one.
|
||||
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
@@ -128,6 +146,9 @@ jobs:
|
||||
- name: Cache npm package downloads
|
||||
id: npm-cache
|
||||
uses: ./.github/actions/npm-cache
|
||||
with:
|
||||
# Linux x64 writer. Parallel Linux jobs restore this key.
|
||||
save: "true"
|
||||
- run: npm ci
|
||||
- name: Restore static task cache
|
||||
if: needs.changes.outputs.code == 'true'
|
||||
@@ -151,14 +172,10 @@ jobs:
|
||||
if: needs.changes.outputs.code == 'true'
|
||||
env:
|
||||
CI_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
CI_STATIC_CONTEXT_SCRIPT: check:ci:fast:tasks
|
||||
run: npm run ci:static-context
|
||||
- if: needs.changes.outputs.code == 'true'
|
||||
run: npm run check:ci
|
||||
- if: needs.changes.outputs.code == 'true'
|
||||
run: npm run verify:test-discovery
|
||||
- name: Check LLDB producer retention and launch contracts
|
||||
if: needs.changes.outputs.code == 'true'
|
||||
run: npm run test:python
|
||||
run: npm run check:ci:fast
|
||||
- if: needs.changes.outputs.code != 'true'
|
||||
run: npm run format:check
|
||||
- name: Save validated static task cache
|
||||
@@ -180,6 +197,72 @@ jobs:
|
||||
if-no-files-found: ignore
|
||||
retention-days: 7
|
||||
|
||||
static-deep:
|
||||
name: Deep static checks
|
||||
needs: changes
|
||||
# Full qualification only. Provider lanes wait on fast static, not this job.
|
||||
if: needs.changes.outputs.tier == 'full' && needs.changes.outputs.code == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
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
|
||||
- name: Restore deep static task cache
|
||||
id: static-cache
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: .turbo/cache/
|
||||
key: rea-static-deep-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.nvmrc', '.npmrc', 'package-lock.json') }}-${{ github.sha }}
|
||||
restore-keys: rea-static-deep-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.nvmrc', '.npmrc', 'package-lock.json') }}-
|
||||
- name: Restore Knip analysis cache
|
||||
id: knip-cache
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: node_modules/.cache/knip
|
||||
# Each trusted commit publishes a new entry. Restore still matches the
|
||||
# configuration prefix from the previous successful full run.
|
||||
key: knip-meta-v1-${{ runner.os }}-${{ hashFiles('knip.json', 'tsconfig.json', 'package.json', 'package-lock.json') }}-${{ github.sha }}
|
||||
restore-keys: knip-meta-v1-${{ runner.os }}-${{ hashFiles('knip.json', 'tsconfig.json', 'package.json', 'package-lock.json') }}-
|
||||
- name: Record deep static task source and execution identity
|
||||
continue-on-error: true
|
||||
env:
|
||||
CI_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
CI_STATIC_CONTEXT_SCRIPT: check:ci:deep:tasks
|
||||
run: npm run ci:static-context
|
||||
- name: Run deep static checks
|
||||
run: npm run check:ci:deep
|
||||
- name: Save validated deep static task cache
|
||||
if: >-
|
||||
success() && steps.npm-cache.outputs.can-save == 'true' &&
|
||||
steps.static-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: .turbo/cache/
|
||||
key: ${{ steps.static-cache.outputs.cache-primary-key }}
|
||||
- name: Save Knip analysis cache
|
||||
if: >-
|
||||
success() && steps.npm-cache.outputs.can-save == 'true' &&
|
||||
steps.knip-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: node_modules/.cache/knip
|
||||
key: ${{ steps.knip-cache.outputs.cache-primary-key }}
|
||||
- name: Upload complete deep static diagnostics
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: static-deep-check-reports
|
||||
path: .cache/static-checks/
|
||||
include-hidden-files: true
|
||||
if-no-files-found: ignore
|
||||
retention-days: 7
|
||||
|
||||
test-source:
|
||||
name: Source tests
|
||||
needs: changes
|
||||
@@ -219,9 +302,11 @@ jobs:
|
||||
# every shard's diagnostics and coverage. Passing PRs run the full suite.
|
||||
fail-fast: ${{ github.event_name == 'pull_request' && needs.changes.outputs.tier == 'fast' }}
|
||||
matrix:
|
||||
# Fast feedback spends the same six test jobs on one source group and
|
||||
# five built groups; full qualification retains six complete shards.
|
||||
shard: ${{ fromJSON(needs.changes.outputs.tier == 'fast' && '["1/5","2/5","3/5","4/5","5/5"]' || '["1/6","2/6","3/6","4/6","5/6","6/6"]') }}
|
||||
# Fast feedback uses one source group and eight built groups so the
|
||||
# slowest built shard job stays inside a ten-minute execution budget.
|
||||
# Classification, build, and report merge sit outside that job.
|
||||
# Full qualification retains six complete shards with coverage.
|
||||
shard: ${{ fromJSON(needs.changes.outputs.tier == 'fast' && '["1/8","2/8","3/8","4/8","5/8","6/8","7/8","8/8"]' || '["1/6","2/6","3/6","4/6","5/6","6/6"]') }}
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
@@ -324,7 +409,9 @@ jobs:
|
||||
with:
|
||||
save: ${{ github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
||||
- run: npm run test:prepare
|
||||
- name: Find available trusted full-suite timing history
|
||||
# Fast and full shards measure different file costs. Download only the
|
||||
# matching history so one tier cannot pack the other with its weights.
|
||||
- name: Find available trusted timing history
|
||||
id: history
|
||||
continue-on-error: true
|
||||
env:
|
||||
@@ -332,13 +419,14 @@ jobs:
|
||||
CI_REPOSITORY: ${{ github.repository }}
|
||||
CI_REPOSITORY_ID: ${{ github.repository_id }}
|
||||
CI_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
|
||||
CI_TIMING_TIER: ${{ needs.changes.outputs.tier }}
|
||||
run: node scripts/ci/test-history.mjs
|
||||
- name: Download previous timing report
|
||||
if: steps.history.outputs.run-id != ''
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: test-timings
|
||||
name: ${{ steps.history.outputs.artifact-name }}
|
||||
run-id: ${{ steps.history.outputs.run-id }}
|
||||
github-token: ${{ github.token }}
|
||||
path: .cache/ci-test-history
|
||||
@@ -373,8 +461,9 @@ jobs:
|
||||
retention-days: 7
|
||||
|
||||
sqlite-runtime:
|
||||
# Preserve all selected coverage, but admit expensive host/provider work
|
||||
# only after static validation. Portable tests still provide early feedback.
|
||||
# Admit expensive host/provider work after fast static validation
|
||||
# (typecheck, lint, format). Deep static does not gate these lanes.
|
||||
# Portable tests still provide early feedback.
|
||||
needs: [changes, static, build]
|
||||
if: needs.changes.outputs.sqlite == 'true'
|
||||
uses: ./.github/workflows/sqlite-runtime.yml
|
||||
@@ -469,11 +558,22 @@ jobs:
|
||||
include-hidden-files: true
|
||||
if-no-files-found: error
|
||||
retention-days: 30
|
||||
# Default-branch runs only. Pull-request artifacts share this name and
|
||||
# the lookup reads one page, so publishing them would hide main.
|
||||
- name: Publish successful timing history
|
||||
if: needs.changes.outputs.code == 'true' && needs.changes.outputs.tier == 'full' && success() && needs.test-shard.result == 'success'
|
||||
if: >-
|
||||
success() &&
|
||||
needs.changes.outputs.code == 'true' &&
|
||||
needs.test-shard.result == 'success' &&
|
||||
(
|
||||
needs.changes.outputs.tier == 'full' ||
|
||||
(needs.changes.outputs.tier == 'fast' && needs.test-source.result == 'success')
|
||||
) &&
|
||||
github.ref == format('refs/heads/{0}', github.event.repository.default_branch) &&
|
||||
(github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: test-timings
|
||||
name: test-timings-${{ needs.changes.outputs.tier }}
|
||||
path: .cache/ci-test-history/test-results.json
|
||||
include-hidden-files: true
|
||||
if-no-files-found: error
|
||||
@@ -577,6 +677,9 @@ jobs:
|
||||
- name: Cache npm package downloads
|
||||
id: npm-cache
|
||||
uses: ./.github/actions/npm-cache
|
||||
with:
|
||||
# ubuntu-latest is the Linux x64 writer above. Other hosts save their own key.
|
||||
save: ${{ matrix.os != 'ubuntu-latest' }}
|
||||
- name: Verify native runtime identity
|
||||
env:
|
||||
EXPECTED_CI_PLATFORM: ${{ matrix.platform }}
|
||||
@@ -713,6 +816,8 @@ jobs:
|
||||
- name: Cache npm package downloads
|
||||
id: npm-cache
|
||||
uses: ./.github/actions/npm-cache
|
||||
with:
|
||||
save: "true"
|
||||
- run: npm ci
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
@@ -858,68 +963,96 @@ jobs:
|
||||
skills/reverse-engineer-anything
|
||||
if-no-files-found: error
|
||||
|
||||
# Provider lanes wait for fast static and the portable build, not deep static.
|
||||
browser:
|
||||
needs: [changes, static, build]
|
||||
if: needs.changes.outputs.browser == 'true'
|
||||
secrets:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
uses: ./.github/workflows/real-browser.yml
|
||||
with:
|
||||
build_cache_artifact: rea-build-cache
|
||||
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
|
||||
|
||||
binary_layout:
|
||||
needs: [changes, static, build]
|
||||
if: needs.changes.outputs.binary_layout == 'true'
|
||||
secrets:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
uses: ./.github/workflows/real-binary-layout.yml
|
||||
with:
|
||||
build_cache_artifact: rea-build-cache
|
||||
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
|
||||
|
||||
evm:
|
||||
needs: [changes, static, build]
|
||||
if: needs.changes.outputs.evm == 'true'
|
||||
secrets:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
uses: ./.github/workflows/real-evm-interface.yml
|
||||
with:
|
||||
build_cache_artifact: rea-build-cache
|
||||
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
|
||||
|
||||
javascript_recovery:
|
||||
needs: [changes, static, build]
|
||||
if: needs.changes.outputs.javascript_recovery == 'true'
|
||||
secrets:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
uses: ./.github/workflows/real-javascript-recovery.yml
|
||||
with:
|
||||
build_cache_artifact: rea-build-cache
|
||||
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
|
||||
|
||||
recorded_crash:
|
||||
needs: [changes, static, build]
|
||||
if: needs.changes.outputs.recorded_crash == 'true'
|
||||
secrets:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
uses: ./.github/workflows/real-recorded-crash.yml
|
||||
with:
|
||||
build_cache_artifact: rea-build-cache
|
||||
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
|
||||
|
||||
web_network:
|
||||
needs: [changes, static, build]
|
||||
if: needs.changes.outputs.web_network == 'true'
|
||||
secrets:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
uses: ./.github/workflows/real-web-network-captures.yml
|
||||
with:
|
||||
build_cache_artifact: rea-build-cache
|
||||
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
|
||||
|
||||
web_runtime:
|
||||
needs: [changes, static, build]
|
||||
if: needs.changes.outputs.web_runtime == 'true'
|
||||
secrets:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
uses: ./.github/workflows/real-web-runtime.yml
|
||||
with:
|
||||
build_cache_artifact: rea-build-cache
|
||||
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
|
||||
|
||||
web_source_map:
|
||||
needs: [changes, static, build]
|
||||
if: needs.changes.outputs.web_source_map == 'true'
|
||||
secrets:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
uses: ./.github/workflows/real-web-source-map.yml
|
||||
with:
|
||||
build_cache_artifact: rea-build-cache
|
||||
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
|
||||
|
||||
agentenv:
|
||||
needs: [changes, static, build]
|
||||
if: needs.changes.outputs.agentenv == 'true'
|
||||
secrets:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
uses: ./.github/workflows/real-agentenv.yml
|
||||
with:
|
||||
build_cache_artifact: rea-build-cache
|
||||
remote_cache_write: ${{ inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')) }}
|
||||
|
||||
nativeaot:
|
||||
needs: [changes, static]
|
||||
@@ -944,6 +1077,7 @@ jobs:
|
||||
[
|
||||
changes,
|
||||
static,
|
||||
static-deep,
|
||||
build,
|
||||
reverse-tools-quality,
|
||||
test-shard,
|
||||
|
||||
@@ -14,6 +14,11 @@ concurrency:
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# Writes only on the default branch for push, schedule, manual dispatch,
|
||||
# and workflow_call. Pull requests get an empty token and remote read-only mode.
|
||||
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
|
||||
|
||||
jobs:
|
||||
mutation:
|
||||
|
||||
@@ -9,12 +9,28 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
remote_cache_write:
|
||||
description: Trusted release qualification may publish the remote cache.
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
TURBO_TOKEN:
|
||||
description: Remote cache token. Only trusted callers pass it.
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# A called workflow sees the caller event, so workflow_call is not an event
|
||||
# name here. Writes when remote_cache_write is set, or on the default branch
|
||||
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
|
||||
# and use the local cache only.
|
||||
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
PIP_DISABLE_PIP_VERSION_CHECK: "1"
|
||||
|
||||
|
||||
@@ -14,6 +14,15 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
remote_cache_write:
|
||||
description: Trusted release qualification may publish the remote cache.
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
TURBO_TOKEN:
|
||||
description: Remote cache token. Only trusted callers pass it.
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -21,6 +30,13 @@ permissions:
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
HUSKY: "0"
|
||||
# A called workflow sees the caller event, so workflow_call is not an event
|
||||
# name here. Writes when remote_cache_write is set, or on the default branch
|
||||
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
|
||||
# and use the local cache only.
|
||||
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
|
||||
REA_VERIFY_LARGE_ELF_MCP: ${{ inputs.large_mcp && '1' || '0' }}
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -8,10 +8,26 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
remote_cache_write:
|
||||
description: Trusted release qualification may publish the remote cache.
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
TURBO_TOKEN:
|
||||
description: Remote cache token. Only trusted callers pass it.
|
||||
required: false
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# A called workflow sees the caller event, so workflow_call is not an event
|
||||
# name here. Writes when remote_cache_write is set, or on the default branch
|
||||
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
|
||||
# and use the local cache only.
|
||||
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -8,6 +8,14 @@ on:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# Writes only on the default branch for push, schedule, manual dispatch,
|
||||
# and workflow_call. Pull requests get an empty token and remote read-only mode.
|
||||
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
|
||||
|
||||
jobs:
|
||||
windows-native-artifact:
|
||||
name: Build Windows bridge security boundary
|
||||
|
||||
@@ -9,6 +9,15 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
remote_cache_write:
|
||||
description: Trusted release qualification may publish the remote cache.
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
TURBO_TOKEN:
|
||||
description: Remote cache token. Only trusted callers pass it.
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -16,6 +25,13 @@ permissions:
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
HUSKY: "0"
|
||||
# A called workflow sees the caller event, so workflow_call is not an event
|
||||
# name here. Writes when remote_cache_write is set, or on the default branch
|
||||
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
|
||||
# and use the local cache only.
|
||||
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
|
||||
|
||||
jobs:
|
||||
real-evm-interface:
|
||||
|
||||
@@ -6,6 +6,11 @@ on:
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# Writes only on the default branch for push, schedule, manual dispatch,
|
||||
# and workflow_call. Pull requests get an empty token and remote read-only mode.
|
||||
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -13,20 +13,8 @@ on:
|
||||
- "src/composition/go.ts"
|
||||
- "src/cli/goCommands.ts"
|
||||
- "src/server/registerGoTools.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/contracts/toolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/artifacts/readStableArtifact*.ts"
|
||||
- "src/artifacts/ArtifactReader.ts"
|
||||
- "src/process/PrivateRuntimeRoot.ts"
|
||||
- "scripts/verify-go-binary.mjs"
|
||||
- "scripts/lib/mcp-verifier-results.mjs"
|
||||
- "scripts/rea.mjs"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
- ".nvmrc"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -38,6 +26,11 @@ concurrency:
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
HUSKY: "0"
|
||||
# Writes only on the default branch for push, schedule, manual dispatch,
|
||||
# and workflow_call. Pull requests get an empty token and remote read-only mode.
|
||||
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
|
||||
|
||||
jobs:
|
||||
real-go-binary:
|
||||
|
||||
@@ -5,6 +5,11 @@ on:
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# Writes only on the default branch for push, schedule, manual dispatch,
|
||||
# and workflow_call. Pull requests get an empty token and remote read-only mode.
|
||||
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -5,6 +5,11 @@ on:
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# Writes only on the default branch for push, schedule, manual dispatch,
|
||||
# and workflow_call. Pull requests get an empty token and remote read-only mode.
|
||||
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -8,6 +8,15 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
remote_cache_write:
|
||||
description: Trusted release qualification may publish the remote cache.
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
TURBO_TOKEN:
|
||||
description: Remote cache token. Only trusted callers pass it.
|
||||
required: false
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
@@ -15,6 +24,13 @@ permissions:
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# A called workflow sees the caller event, so workflow_call is not an event
|
||||
# name here. Writes when remote_cache_write is set, or on the default branch
|
||||
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
|
||||
# and use the local cache only.
|
||||
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
|
||||
NODE_OPTIONS: "--max-old-space-size=768"
|
||||
RAYON_NUM_THREADS: "1"
|
||||
|
||||
|
||||
@@ -20,21 +20,17 @@ on:
|
||||
- "src/composition/captures.ts"
|
||||
- "src/cli/captureCommands.ts"
|
||||
- "src/server/registerCaptureTools.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/server/sessionAvailabilityPolicy.ts"
|
||||
- "src/application/CapabilityInventory.ts"
|
||||
- "src/contracts/toolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/process/**"
|
||||
- "src/artifacts/readStableArtifact.ts"
|
||||
- "scripts/verify-packet-capture.mjs"
|
||||
- ".github/workflows/real-packet-capture.yml"
|
||||
- "package*.json"
|
||||
- ".nvmrc"
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# Writes only on the default branch for push, schedule, manual dispatch,
|
||||
# and workflow_call. Pull requests get an empty token and remote read-only mode.
|
||||
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
|
||||
concurrency:
|
||||
group: real-packet-capture-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
@@ -9,6 +9,15 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
remote_cache_write:
|
||||
description: Trusted release qualification may publish the remote cache.
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
TURBO_TOKEN:
|
||||
description: Remote cache token. Only trusted callers pass it.
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -16,6 +25,13 @@ permissions:
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
HUSKY: "0"
|
||||
# A called workflow sees the caller event, so workflow_call is not an event
|
||||
# name here. Writes when remote_cache_write is set, or on the default branch
|
||||
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
|
||||
# and use the local cache only.
|
||||
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
|
||||
UV_CONCURRENT_BUILDS: "1"
|
||||
UV_CONCURRENT_INSTALLS: "1"
|
||||
UV_PYTHON_DOWNLOADS: never
|
||||
|
||||
@@ -13,21 +13,17 @@ on:
|
||||
- "src/composition/signals.ts"
|
||||
- "src/cli/signalCommands.ts"
|
||||
- "src/server/registerSignalTools.ts"
|
||||
- "src/server/createServer.ts"
|
||||
- "src/server/sessionAvailabilityPolicy.ts"
|
||||
- "src/application/CapabilityInventory.ts"
|
||||
- "src/contracts/toolContracts.ts"
|
||||
- "src/contracts/toolEffects.ts"
|
||||
- "src/cli.ts"
|
||||
- "src/cliCommandNames.ts"
|
||||
- "src/process/**"
|
||||
- "src/artifacts/readStableArtifact.ts"
|
||||
- "scripts/verify-signal-recording.mjs"
|
||||
- ".github/workflows/real-signal-recording.yml"
|
||||
- "package*.json"
|
||||
- ".nvmrc"
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# Writes only on the default branch for push, schedule, manual dispatch,
|
||||
# and workflow_call. Pull requests get an empty token and remote read-only mode.
|
||||
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
|
||||
concurrency:
|
||||
group: real-signal-recording-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
@@ -12,6 +12,11 @@ permissions:
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# Writes only on the default branch for push, schedule, manual dispatch,
|
||||
# and workflow_call. Pull requests get an empty token and remote read-only mode.
|
||||
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
|
||||
|
||||
jobs:
|
||||
real-termux:
|
||||
|
||||
@@ -9,12 +9,28 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
remote_cache_write:
|
||||
description: Trusted release qualification may publish the remote cache.
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
TURBO_TOKEN:
|
||||
description: Remote cache token. Only trusted callers pass it.
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# A called workflow sees the caller event, so workflow_call is not an event
|
||||
# name here. Writes when remote_cache_write is set, or on the default branch
|
||||
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
|
||||
# and use the local cache only.
|
||||
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -7,11 +7,27 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
remote_cache_write:
|
||||
description: Trusted release qualification may publish the remote cache.
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
TURBO_TOKEN:
|
||||
description: Remote cache token. Only trusted callers pass it.
|
||||
required: false
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# A called workflow sees the caller event, so workflow_call is not an event
|
||||
# name here. Writes when remote_cache_write is set, or on the default branch
|
||||
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
|
||||
# and use the local cache only.
|
||||
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
|
||||
jobs:
|
||||
real-web-runtime:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -7,11 +7,27 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
remote_cache_write:
|
||||
description: Trusted release qualification may publish the remote cache.
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
TURBO_TOKEN:
|
||||
description: Remote cache token. Only trusted callers pass it.
|
||||
required: false
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# A called workflow sees the caller event, so workflow_call is not an event
|
||||
# name here. Writes when remote_cache_write is set, or on the default branch
|
||||
# for push, schedule, and manual dispatch. Open pull requests stay empty-token
|
||||
# and use the local cache only.
|
||||
TURBO_TOKEN: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (inputs.remote_cache_write == true || (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'))) && 'local:rw,remote:rw' || 'local:rw' }}"
|
||||
jobs:
|
||||
real-web-source-map:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -21,6 +21,12 @@ on:
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
# Remote cache stays local when either value is empty.
|
||||
# Writes only on the default branch for push, schedule, manual dispatch,
|
||||
# and workflow_call. Pull requests get an empty token and remote read-only mode.
|
||||
TURBO_TOKEN: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && secrets.TURBO_TOKEN || '' }}"
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_CACHE: "${{ (github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call')) && 'local:rw,remote:rw' || 'local:rw,remote:r' }}"
|
||||
|
||||
concurrency:
|
||||
group: release-${{ inputs.release_branch || github.event.pull_request.merge_commit_sha || github.ref_name }}
|
||||
@@ -301,8 +307,11 @@ jobs:
|
||||
contents: read
|
||||
actions: read
|
||||
uses: ./.github/workflows/ci.yml
|
||||
secrets:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
with:
|
||||
source_sha: ${{ needs.merged-release.outputs.sha || needs.release-please.outputs.sha }}
|
||||
remote_cache_write: true
|
||||
|
||||
rehearsal:
|
||||
name: Rehearse qualified release on a clean runner
|
||||
|
||||
@@ -174,34 +174,36 @@ 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 |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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) |
|
||||
| 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 |
|
||||
| Model artifacts | GGUF / safetensors metadata, tensor declarations and derived totals | Built-in header inspection; optional full-file hash; [model files](docs/model-artifacts.md) |
|
||||
| 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) |
|
||||
| PyInstaller executables | CArchive members, Python version, entry scripts and extracted bytecode | None (static only); [Python packaged applications](docs/python-packaged-applications.md) |
|
||||
|
||||
Static JavaScript and .NET inspection read the supplied files without running
|
||||
the application. Runtime capture runs or interacts with the selected target
|
||||
|
||||
@@ -30,6 +30,7 @@ export default defineConfig({
|
||||
{ text: "Native binaries", link: "/native-investigation" },
|
||||
{ text: "Go build metadata", link: "/go-binaries" },
|
||||
{ text: "IL2CPP metadata", link: "/il2cpp-metadata" },
|
||||
{ text: "Model artifacts", link: "/model-artifacts" },
|
||||
{ text: "Rust dependencies", link: "/rust-binaries" },
|
||||
{
|
||||
text: "JavaScript and Electron",
|
||||
@@ -49,6 +50,10 @@ export default defineConfig({
|
||||
{ text: "Electron fuses", link: "/electron-fuses" },
|
||||
{ text: "Process capture", link: "/process-capture" },
|
||||
{ text: ".NET assemblies", link: "/managed-code-analysis" },
|
||||
{
|
||||
text: "Python packaged applications",
|
||||
link: "/python-packaged-applications",
|
||||
},
|
||||
{ text: "Android applications", link: "/android-analysis" },
|
||||
{ text: "Apple applications", link: "/apple-application-analysis" },
|
||||
{ text: "Desktop installations", link: "/desktop-applications" },
|
||||
|
||||
@@ -180,8 +180,21 @@ 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 `LC_LAZY_LOAD_DYLIB_INFO` (`0x3a`). That command stores its
|
||||
install name in a linkedit payload rather than in the load command itself.
|
||||
Its flag and chain-start image offsets must lie in the slice VM range, which
|
||||
is dyld's one-page floor extended by every segment except `__LINKEDIT`,
|
||||
measured from the `__TEXT` vmaddr. Payload strings must start after the 24-byte
|
||||
header. The load-path NUL must occur before the payload's final byte, matching
|
||||
dyld's scan. Distinct payload bytes and payload-range lookups stay within the 16 MiB
|
||||
limit, and a file range already read is reused. String scanning and UTF-8
|
||||
checks count every reference, including repeats and overlaps, and stop at that
|
||||
same aggregate. A slice retains at most 1024 of these commands; further copies are
|
||||
unsupported instead of becoming one traced edge each. Symbol references,
|
||||
including repeats, stop at 65536 per slice. A larger table is unsupported
|
||||
instead of caching one string per offset.
|
||||
Nothing is executed and no Apple tool is required, so the tool also works off
|
||||
macOS.
|
||||
|
||||
Resolution follows dyld:
|
||||
|
||||
@@ -200,30 +213,55 @@ Resolution follows dyld:
|
||||
Each edge lists every candidate path tried, with its outcome: `resolved`,
|
||||
`absent`, `not-mach-o`, `malformed`, `unsupported` (recognized Mach-O outside
|
||||
parser coverage, such as big-endian images), `architecture-missing`,
|
||||
`outside-target`, `escapes-target`, or `undetermined`. Slices follow dyld's
|
||||
graded architectures, so an `x86_64h` process also loads `x86_64`. The edge's `resolution` is one of:
|
||||
`not-loadable`, `platform-mismatch`, `outside-target`, `escapes-target`, or
|
||||
`undetermined`. `not-loadable` means a
|
||||
matching-architecture Mach-O image was found but it is not a dylib and cannot
|
||||
satisfy a dylib dependency. `platform-mismatch` means a matching-architecture
|
||||
dylib was found but its observed platform is incompatible with the process.
|
||||
These outcomes reject that candidate; dyld search may continue and resolve a
|
||||
later candidate. Candidate outcomes describe individual search paths, while
|
||||
`resolution.status` describes the final edge. Slices follow dyld's graded
|
||||
architectures, so an `x86_64h` process also loads `x86_64`. The edge's
|
||||
`resolution` is one of:
|
||||
|
||||
| Status | Meaning |
|
||||
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `resolved` | An image inside the analyzed root loads, and no earlier candidate was undeterminable. |
|
||||
| `conditional` | An image inside the root resolves, but an earlier candidate outside the root (for example `/usr/lib/swift`) would win if it exists on the running system. |
|
||||
| `unresolved` | Every candidate is definitively absent or unusable inside the root. |
|
||||
| `undetermined` | No candidate resolves inside the root, and some candidate lies outside it or depends on the environment. |
|
||||
| Status | Meaning |
|
||||
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `resolved` | An image inside the analyzed root loads, and no earlier candidate was undeterminable. |
|
||||
| `conditional` | An in-root image may load, but an earlier outside candidate, unmodeled environment input, or unknown platform compatibility can change which image dyld loads. |
|
||||
| `unresolved` | Every candidate is definitively absent or unusable inside the root. |
|
||||
| `undetermined` | No candidate resolves inside the root, and some candidate lies outside it or depends on the environment. |
|
||||
|
||||
A `conditional` image is still traced, because it is the image the analyzed
|
||||
root supplies. Every edge below it has `loader_conditional: true`, and a
|
||||
request that reuses it by install name is `conditional` too. Findings on those
|
||||
edges say that they apply only if the conditional image loads.
|
||||
A `conditional` image is still traced, because it is an in-root candidate
|
||||
that may load. Every edge below it has `loader_conditional: true`, and a
|
||||
request that reuses it by install name is `conditional` too. An in-root dylib
|
||||
with no platform load command remains unknown unless the root is an executable
|
||||
with observed macOS platform and the candidate’s CPU type is x86_64, i386, or arm64. In that macOS context,
|
||||
[Apple's dyld](https://github.com/apple-oss-distributions/dyld/blob/fd8d0c4d52320ebf64db34f3cb280310d905c5ae/mach_o/UnsafeHeader.cpp#L1137-L1145) supplies an implicit macOS platform to legacy dylibs on those CPU
|
||||
types. The candidate records `platform_basis: "macos-dyld-implicit"`; the
|
||||
reported slice fields `platform: null` and `platforms: []` remain the parsed
|
||||
facts. For other process contexts the platform remains unknown, coverage is
|
||||
partial, and the candidate is not treated as incompatible. If a later known
|
||||
candidate resolves, it remains the traced image with conditional resolution;
|
||||
the trace does not claim the unknown candidate's dependency graph. Findings on
|
||||
conditional edges say that they apply only if the conditional image loads.
|
||||
|
||||
Absolute install names such as `/usr/lib/libSystem.B.dylib` are
|
||||
`outside-target`. On macOS 11 and later most of them live in the dyld shared
|
||||
cache rather than on disk, so REA does not check them against the host.
|
||||
Absolute install names that name a path beneath the analyzed root are checked
|
||||
within that root, with symlinks and `..` resolved by the same bounded tree
|
||||
resolver. Absolute paths outside the root, such as
|
||||
`/usr/lib/libSystem.B.dylib`, are `outside-target`; on macOS 11 and later most
|
||||
system libraries live in the dyld shared cache rather than on disk, so REA does
|
||||
not check them against the host. Mach-O path strings are not converted from
|
||||
`file:` URLs or looked up through unrelated host paths.
|
||||
|
||||
`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
|
||||
loads them on first use; an unresolved one is not a launch failure.
|
||||
`LC_LAZY_LOAD_DYLIB` and `LC_LAZY_LOAD_DYLIB_INFO` dependencies are resolved
|
||||
but not traversed, because dyld loads them on first use; an unresolved one is
|
||||
not a launch failure. `LC_LAZY_LOAD_DYLIB_INFO` has no compatibility version,
|
||||
so those fields are null. Its may-be-missing flag is reported as `weak`, and
|
||||
an unresolved dependency with that flag is not a first-use failure: dyld
|
||||
continues if the image is absent.
|
||||
- `earlier-rpath-candidate-absent`: a compatible Mach-O placed at an earlier
|
||||
modeled search path could take precedence, subject to unmodeled search inputs
|
||||
and code-signing library validation. These findings do not establish which
|
||||
|
||||
@@ -19,8 +19,24 @@ Inspect native SAZ or HAR 1.2 offline with REA, or connect Everywhere's separate
|
||||
- `analyze_web_bundle` / `rea analyze-web-bundle` parses selected script artifacts without execution and derives chunk edges, route and endpoint candidates, vendor fingerprints, static WebMCP declarations, and optionally source-map/original-source evidence.
|
||||
- `observe_web_session` / `rea observe-web-session` arms an observation window of the requested duration for an external user action and records ordered reload, SPA navigation, redirect, failure, lifecycle, and target-termination metadata.
|
||||
- `discover_webmcp_tools` / `rea discover-webmcp-tools` uses the experimental CDP WebMCP domain to return every in-scope registration and its complete observed `input_schema` declaration and canonical `input_schema_sha256` digest inline. Declarations are untrusted JSON data, including declared types, required fields, enums, examples and reference URLs. REA does not resolve schema references. These fields replace `input_schema_shape`. Registrations with the same name and URL in distinct observed frames retain separate identities. `tool_key` includes the observed frame ID; it is not a stable identity across navigation or browser sessions. Same-frame registration updates replace that owner only, and removal leaves other owners intact. Declared `readOnly`, `untrustedContent`, `consequential`, and `autosubmit` annotation hints are reported per registration: `true`/`false` are declared values, `null` means the declaration did not provide the hint in that capture, and a missing `consequential` key means the hint was not recorded, either in a historical capture or because the observed annotation was invalid. Consult discovery completeness and exclusions to distinguish these cases. Malformed annotation values retain the registration with incomplete coverage and an `invalid_protocol_value` exclusion. Other malformed protocol payloads can fail discovery explicitly. REA never exposes `WebMCP.invokeTool`.
|
||||
|
||||
- `compare_web_captures` / `rea compare-web-captures` compares passive captures or exact-step-aligned browser scenarios. Scenario results commit reproducible literal normalization and expose alignment failures plus artifact-level action, screenshot, DOM, accessibility, URL, history, storage, and event differences. Missing or truncated evidence is never treated as equivalence.
|
||||
|
||||
For `analyze_web_bundle`, each `webmcp_declarations` entry's
|
||||
`schema_property_names` is a source-derived projection of statically known own
|
||||
property keys, not schema validation. `schema_property_names_coverage` reports
|
||||
how complete that projection is: `complete` means a literal properties object
|
||||
was fully enumerated (including an empty literal); `partial` means a literal
|
||||
object was available but some computed or spread membership was unresolved,
|
||||
and the name array retains keys that were known; `unknown` means the schema or
|
||||
properties owner could not be enumerated as an object literal, including
|
||||
explicit non-object values; `not_present` means no effective schema or own
|
||||
`properties` member was present after the `inputSchema ?? input_schema` alias
|
||||
selection. `not_present` describes this projection only: it does not validate a
|
||||
schema or rule out properties expressed through other schema constructs. Older
|
||||
observations may omit the coverage field; treat omission as `unknown` rather
|
||||
than inferring completeness from the array.
|
||||
|
||||
Passive DOM comparison checks complete node inventories and complete frame
|
||||
parent/child structure independently. Frame IDs and sibling enumeration order
|
||||
are capture-local and do not establish a change; duplicate IDs, missing parents,
|
||||
@@ -201,6 +217,11 @@ rea inspect-web-page http://127.0.0.1:9222 TARGET_ID \
|
||||
--json
|
||||
```
|
||||
|
||||
Passive observation honors the requested duration using cancellable waits within
|
||||
the runtime's timer range. Long windows stay pending across successive waits;
|
||||
caller cancellation, target termination or leaving the allowed origins ends
|
||||
collection early. `window_elapsed` identifies completion of the requested wait.
|
||||
|
||||
Static bundle analysis and a user-driven observation window are separate operations:
|
||||
|
||||
```bash
|
||||
@@ -283,6 +304,20 @@ Call `list_browser_targets` first because target IDs are browser-instance-specif
|
||||
|
||||
REA establishes a main-frame origin boundary before enabling Runtime, Debugger, or Network observation, rechecks it before document capture, and validates it again afterward. If the main frame navigates during final capture, REA discards the mixed result and returns `target_changed`.
|
||||
|
||||
The web-session timeline uses CDP monotonic seconds where the event producer
|
||||
provides a comparable timestamp; absent or invalid timestamps are `null` rather
|
||||
than a synthetic zero. Events buffered while REA samples the window boundary are
|
||||
retained only when their timestamp places them after that boundary; older events
|
||||
are excluded. A buffered event without a comparable timestamp may remain as
|
||||
contextual evidence with `timestamp: null`, accompanied by a limitation stating
|
||||
that it does not establish activity in the selected window. Main-frame scope
|
||||
enforcement still applies to contextual navigation events. Network redirect
|
||||
events retain their paired prior-hop response context when the timestamped
|
||||
next-request event falls inside the window. The pre-arm timeline buffer is bounded
|
||||
by original CDP message bytes. If clock sampling completes after that budget is
|
||||
exhausted, the operation returns `payload_limit` instead of a partial window.
|
||||
Cancellation and clock-request failures retain their own outcomes.
|
||||
|
||||
## Data minimization
|
||||
|
||||
The Link projection parses repeated CDP header field lines independently and
|
||||
@@ -326,8 +361,8 @@ REA retains local observation data and removes only URL userinfo credentials, ex
|
||||
WebSocket stream inventories and connection counts likewise require complete
|
||||
WebSocket coverage. Redirect resource types use only shared known metadata:
|
||||
an omitted type leaves equality unknown rather than proving a modification.
|
||||
- Console observations with a stack source retain call type, argument types, timestamp, and source location. When console text capture is selected, already-delivered primitive values are retained verbatim; objects, getters, and remote properties are never expanded.
|
||||
- WebSocket observations retain direction, opcode, and payload byte length. When shape capture is selected, text frames are classified as text or complete value-free JSON shape; binary bytes, hashes, prefixes, and raw frames are never retained.
|
||||
- Console observations with a stack source retain call type, argument types, the CDP `Runtime.Timestamp` in milliseconds since Unix epoch, and source location. An absent or invalid timestamp is `null` and makes `console_events` incomplete; an observed zero remains zero. This epoch-millisecond clock is separate from the monotonic-second timestamps in `observe-web-session`. When console text capture is selected, already-delivered primitive values are retained verbatim; objects, getters, and remote properties are never expanded.
|
||||
- WebSocket observations retain direction, opcode, and payload byte length. A missing or malformed CDP opcode is returned as `null`, with opcode-dependent payload byte length and shape also `null`, and marks `websocket_frames` incomplete. Missing or wrong-typed payload data is not treated as empty; its byte length is `null`, and a text payload shape is unknown. Numeric opcode `0` and an observed empty payload remain distinct from missing data. Opcode is nullable in the public inspection schema, so clients must handle unknown protocol values. When shape capture is selected, text messages are classified as text or complete value-free JSON shape; binary bytes, hashes, prefixes, and raw payloads are never retained.
|
||||
- Storage observations always redact values. Key names, IndexedDB names, and cache names require `include_storage_keys`; stable content fingerprints require the additional `include_storage_fingerprints` selection. Cookie fingerprints cover cookies applicable to the current main-frame URL, including its path, rather than every cookie path on that origin. Other storage remains scoped to the origin. Cache fingerprints hash all decoded response bytes, including bodies above 64 KiB, subject to the [64 MiB per-message CDP limit](#request-boundary). Missing or undecodable cache bodies and partial IndexedDB remote objects make fingerprint coverage incomplete, so identical observations remain `unknown`.
|
||||
- Unavailable or malformed storage inventories mark `storage_keys` unavailable, even without fingerprints. Comparisons suppress inferred key additions and removals while preserving modifications proven by complete fingerprints on both sides. Previously exported captures are not retroactively corrected; recapture them to obtain these coverage markers.
|
||||
- Script metadata is included only when CDP supplies a URL on an allowed origin. Stable keys exclude transient CDP script IDs, and exact transient raw URLs are used only during script/resource reconciliation. URL-less scripts are excluded because their origin cannot be established. When CDP supplies an execution-context association, the accepted script retains its authorized frame ID for later attribution. Script metadata admission bounds retained identity and source-map strings to 8 MiB and 50,000 records. When a source-map URL cannot fit, the script identity remains and source-map coverage reports `resource_budget_exhausted`; rejected script observations are counted without retaining their IDs. Source content is omitted unless explicitly requested and is returned inline as a self-verifying artifact with its SHA-256 digest.
|
||||
|
||||
+19
-4
@@ -103,6 +103,19 @@ Formats 2, 3 and 4 retain header, directory and integrity facts. Compact deliver
|
||||
keeps complete Evidence in the requested bundle. See [Godot PCK](godot-pck.md)
|
||||
for embedded packs, batch queries and unknown encrypted coverage.
|
||||
|
||||
Inspect local model declarations without loading weights:
|
||||
|
||||
```bash
|
||||
rea inspect-model-artifact /absolute/path/model.gguf --json
|
||||
rea inspect-model-artifact /absolute/path/model.safetensors --hash full --json
|
||||
```
|
||||
|
||||
Default hashing reads only headers and leaves full-file identity unknown.
|
||||
`--hash full` streams the complete file for SHA-256. Both return typed metadata,
|
||||
original tensor declarations and derived totals. MCP summaries retain complete
|
||||
Evidence for filtered metadata, tensor and type-total pages; CLI compact delivery
|
||||
uses `--detail summary --evidence_output FILE`. See [model files](model-artifacts.md).
|
||||
|
||||
Inspect Unity IL2CPP metadata names without Unity or a disassembler:
|
||||
|
||||
```bash
|
||||
@@ -483,10 +496,12 @@ error and exit `1`, as does a listener that fails after binding.
|
||||
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 write that fails after the first file (a permission, disk-space or
|
||||
pathname-length failure on a later path) propagates as the host's failure,
|
||||
naming its cause and, when it failed on one bundle file or the package
|
||||
tarball, that file. A failure while syncing the finished folder names only its
|
||||
cause. Either way the partial folder stays 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
|
||||
|
||||
@@ -457,13 +457,22 @@ disposable HOME and CODEX_HOME under `~/.cache/rea-agent-evaluations/`. Set
|
||||
`REA_AGENT_EVAL_ROOT` to choose another parent directory outside the OS temporary
|
||||
directory: current Codex refuses helper aliases under `/tmp`, preventing shell
|
||||
commands and skill reads even if MCP works. Each owned run directory is removed
|
||||
after verification unless fixture retention is requested. Cleanup attempts the
|
||||
after verification unless fixture retention is requested. Commands share the
|
||||
owned-process supervisor: native ownership preparation has its own bounded
|
||||
deadline before each short command budget, and timeouts join bounded process-tree
|
||||
and output cleanup before returning. If cleanup remains incomplete, the lane retries its retained
|
||||
owner and preserves the run directory (including disposable auth) rather than
|
||||
removing files still in use; the error retains ownership and recovery details.
|
||||
Command stderr is limited to 32 KiB and combined version output to 8 KiB;
|
||||
exceeding these diagnostic budgets fails explicitly. Scenario JSON events stream
|
||||
to the evaluator without retaining a second raw stdout copy. Cleanup attempts the
|
||||
remaining run tree even if auth removal fails, and reports unresolved cleanup
|
||||
together with the original verification failure. The lane
|
||||
installs the packaged skill and MCP registration through public `rea setup`,
|
||||
rather than copying a skill into a project that also inherits the caller's
|
||||
skills and configuration. Only authentication is copied, when present, and it
|
||||
is removed even with `REA_AGENT_EVAL_KEEP_FIXTURES=true`. No user configuration,
|
||||
is removed after confirmed process cleanup even with
|
||||
`REA_AGENT_EVAL_KEEP_FIXTURES=true`. No user configuration,
|
||||
rules, plugins or history is copied; shell snapshots and additional agents are
|
||||
disabled. Existing API-key environment authentication remains available.
|
||||
The native missing-provider scenario preapproves only `open_binary` and
|
||||
|
||||
@@ -91,17 +91,20 @@ 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. |
|
||||
| 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 | A recognized IL2CPP metadata magic (with the raw version when complete) 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
|
||||
When the four-byte metadata magic is present but version bytes are incomplete,
|
||||
the fact retains `raw_version: null`; it does not offer an IL2CPP metadata
|
||||
inspection probe. A complete magic/version prefix 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).
|
||||
|
||||
@@ -6,6 +6,22 @@ For remote devices, pass a host with an optional port (`host[:port]`; bracket IP
|
||||
|
||||
MCP exposes `list_frida_devices`, `list_frida_processes`, `start_frida_session`, `load_frida_script`, `resume_frida_session`, `unload_frida_script`, `frida_session_status`, and `close_frida_session`. A spawned process starts paused so scripts can load first. Closing unloads session scripts and detaches. If a spawn remains paused, REA resumes it only after script cleanup or session detach confirms the instrumentation is released; if both cleanup paths fail, the target stays paused and the cleanup error is retained. REA never kills the target. MCP sessions end with the server process. The one-shot CLI command `instrument-with-frida` accepts one inline script or script file, observes messages for a bounded duration, and returns Evidence with the source hash and captured messages.
|
||||
|
||||
For state and capture-coverage checks, pass `{"session_id":"SESSION_ID","detail":"summary"}` to `frida_session_status`. Each call reads the observed session state and retains the complete observation. The reply is an `inspect_analysis_view` Evidence summary with the session identity, state, scripts, `messages_retained`, and `messages_truncated`; message payloads remain in its `parent_evidence_id`. The count covers retained queued messages, not every target event. Omitted `detail` or `detail=complete` preserves the existing complete reply.
|
||||
|
||||
A summary's selected fields look like this (excerpt):
|
||||
|
||||
```json
|
||||
{
|
||||
"summary": {
|
||||
"state": "running",
|
||||
"messages_retained": 50,
|
||||
"messages_truncated": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use `get_evidence_bundle` with `detail="summary"` and `filters={"evidence_id":"PARENT_ID"}` to discover parent metadata without message payloads. Filters apply only to summary delivery. Use `export_evidence_bundle` for the complete canonical bundle, then select the record whose `evidence_id` matches `normalized_result.parent_evidence_id` from the status summary. To use the existing CLI `inspect-analysis-view`, extract that record into a standalone Evidence JSON file and select it as an Evidence-file source with `view={"kind":"summary"}`. Reading retained Evidence does not refresh a live session or verify a process after exit or PID reuse.
|
||||
|
||||
Failed startup cleanup retains acquired Frida handles, including a paused spawn that has not attached, for later cleanup retries. Its remote connection stays leased until session detach and target resume complete. CLI commands await provider cleanup before returning; MCP retains these resources for cleanup when its server closes. Server shutdown stops admitting new Frida operations and waits for admitted startup, discovery, and queued work before releasing their resources. A failed cleanup remains reported and can be retried by closing the server again, rather than treating an unregistered session as fully released.
|
||||
|
||||
Frida scripts execute inside the selected target with that process's memory, filesystem, and network authority. Message capture is bounded to 16 MiB and reports truncation. Script source is not stored in Evidence; its selected path and SHA-256 digest are. For a file, `source_sha256` identifies the original selected bytes. Invalid UTF-8 continues to use replacement decoding; Evidence then records that limitation and `raw_result.source_representation` with the encoding, decoding mode, and `loaded_source_sha256` of the UTF-8 string passed to Frida. Valid UTF-8 files and inline scripts do not need this additional representation fact. Evidence retains non-authentication target selection such as device or endpoint, mode, program or PID, and spawn arguments. Authentication values remain excluded. Results report the observed session and messages, while unsupported platform metadata remains unknown. A request cancelled before a Frida operation starts has no provider effects. If cancellation arrives during a native Frida call, REA waits for that call to return, then closes any session it created and returns the observed session as a partial observation. A cancelled script load on an existing session releases only its new script before queued operations can run; the session and previously loaded scripts remain available. If cancellation is observed when the native call returns, partial Evidence retains the source identity and captured messages even if that call rejects, including messages emitted during awaited cleanup. The caller's cancellation reason remains primary and a native rejection is retained as a sanitized diagnostic. A failure already observed before cleanup stays primary if cancellation arrives during cleanup. The cancellation cutoff is REA's observation of native completion, rather than a claim about unobservable native event order. `script_id` is REA's load-attempt identity. When creation rejects without returning a handle, `script_cleanup: "no_handle"` means REA acquired no handle to unload; it does not prove that the engine performed no work. Once the provider commits a successful load, later request cancellation does not unload that script; use `unload_frida_script` to release it. If script cleanup fails, the script remains owned and a paused target cannot resume until the pending instrumentation is released. Queued script loads, resumes, and unloads check cancellation before dispatching native effects. Cancellation Evidence separately reports whether a target resume was observed, whether instrumentation cleanup completed, and that final target liveness remains unknown. REA does not kill the target. Remote discovery retains its observations and reports a separate cleanup error if the temporary connection cannot be removed.
|
||||
|
||||
@@ -19,6 +19,56 @@ requires an absolute filesystem path and preserves that exact selection:
|
||||
}
|
||||
```
|
||||
|
||||
For build identity and counts, select `detail: "summary"`. MCP retains the same
|
||||
complete Evidence and returns `normalized_result.parent_evidence_id`. Read
|
||||
selected dependency versions without reopening the file:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_analysis_view",
|
||||
"arguments": {
|
||||
"source": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "ev_<parent digest>"
|
||||
},
|
||||
"view": {
|
||||
"kind": "go",
|
||||
"collection": "dependencies",
|
||||
"paths": ["golang.org/x/crypto"],
|
||||
"fields": ["path", "version", "replacement"],
|
||||
"offset": 0,
|
||||
"limit": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Go views support `dependencies` (exact `paths`) and `settings` (exact `keys`),
|
||||
optional original `indices`, field selection, and pagination. Filters intersect;
|
||||
empty filter arrays select no rows and `fields: []` returns only row identity.
|
||||
Each row keeps its original `index`, scoped to the parent Evidence and collection;
|
||||
duplicate module paths and setting keys keep distinct indices. Page offsets
|
||||
count matching records. Exhausting retained rows does not establish complete
|
||||
runtime dependencies. Original bytes, unknown lines and all complete metadata
|
||||
remain in the parent and can be exported as an Evidence bundle.
|
||||
|
||||
One-shot CLI summaries save complete Evidence before returning the projection:
|
||||
|
||||
```bash
|
||||
rea inspect-go-binary ./application --detail summary --evidence_output ./go-evidence.json --json
|
||||
```
|
||||
|
||||
`--evidence_output` is required for CLI summary delivery and never overwrites an
|
||||
existing file. The default CLI/MCP reply remains complete.
|
||||
|
||||
[Measured workflows](../experiments/analysis-efficiency/go-build-views.md) include
|
||||
catalog repetition and extra calls. Identity/count questions benefit from a
|
||||
single summary. In the tested Codex full-catalog configuration, a compound
|
||||
question about identity and one dependency uses fewer tokens in captured request JSON with
|
||||
one complete inspection than with a summary followed by a lookup. The complete
|
||||
reply remains useful for that initial question; retained views support later
|
||||
selected reads. Token figures are serialization estimates, not model usage.
|
||||
|
||||
## Observations
|
||||
|
||||
The inline Evidence result contains the artifact path, SHA-256, byte length,
|
||||
|
||||
@@ -41,6 +41,15 @@ rea function /absolute/path/to/program main --json
|
||||
rea decompile /absolute/path/to/program main --json
|
||||
```
|
||||
|
||||
If the registration cannot be read or validated, provider diagnostics retain its
|
||||
selected `registration_path` and the configuration owner's setting/constraint
|
||||
facts in `configuration_details.settings`, plus `configuration_remediation`.
|
||||
`configured` means a path was selected, not that it is valid or an IDA connection
|
||||
was established; failed registrations remain unavailable. Correct
|
||||
`REA_IDA_MCP_CONFIG` in the next CLI invocation's environment or the MCP server's
|
||||
launch environment. Restart/reconnect that server after changing its environment;
|
||||
an export in another shell does not update a running server.
|
||||
|
||||
Open the **original input binary** in REA, not an `.idb` or `.i64` database. Its SHA-256 must match IDA's recorded input hash. REA checks the attached identity and image base around each analysis request. A mismatch or target switch fails with recovery guidance. The original input must remain readable by REA; WSL can use its mounted alias while the GUI runs on Windows. Attached profiles, including native mode, refuse universal Mach-O inputs at admission: every slice shares the recorded whole-file hash, so REA cannot verify which slice the GUI loaded. Open a thin file extracted with `lipo -thin` in both IDA and REA, or use headless mode, which analyzes the selected slice.
|
||||
|
||||
REA's close operation releases its MCP connection or proxy. It never saves or closes the existing GUI database. External edits remain visible because results are live and are not replayed from snapshots. These checks do not lock the GUI or prove that the database is unpatched.
|
||||
|
||||
+13
-13
@@ -47,19 +47,19 @@ For runtime instrumentation, see the [Frida guide](./frida-instrumentation.md).
|
||||
|
||||
## Choose a guide
|
||||
|
||||
| Task | Guides |
|
||||
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Identify a desktop installation | [Runtime candidates, evidence and follow-up files](desktop-applications.md) |
|
||||
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [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) |
|
||||
| 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), [Python packages](python-packaged-applications.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).
|
||||
|
||||
@@ -340,6 +340,13 @@ A scoped report has `scope.mode: "explicit"`. Its `scope_checks` determine
|
||||
`environment_healthy` summarizes the full audit. `--target` adds a target check,
|
||||
but the report remains audit-wide unless a scope option is also supplied.
|
||||
|
||||
An invalid `REA_LOG_LEVEL` fails every scope, including one client or the
|
||||
installed skill, because the executable rejects it before any command. A
|
||||
selected client also fails for every other setting that MCP startup rejects,
|
||||
including `REA_GHIDRA_STARTUP_TIMEOUT_MS` and `REA_HOPPER_STARTUP_TIMEOUT_MS`.
|
||||
Those settings still fail a selected provider and the audit-wide result. A
|
||||
skill-only report leaves them informational.
|
||||
|
||||
Follow the remediation for the failed check. If a native target has several
|
||||
available providers, choose one with `--provider` on the CLI or `provider_id`
|
||||
on `open_binary`. A `capability_unavailable` failure carries
|
||||
|
||||
+32
-3
@@ -95,9 +95,10 @@ 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
|
||||
the operation and the REA server/provider deadline; inspect partial Evidence
|
||||
and reported cleanup state, then follow producer-specific remediation or reduce
|
||||
supported work. Generic errors do not assume adjustable timeout controls. Raising
|
||||
only the MCP client's deadline cannot extend this budget. 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
|
||||
@@ -239,6 +240,23 @@ delivery for responses that fit that buffer and Node's single-string limit.
|
||||
It does not change the client's buffer, analysis coverage, or retained content.
|
||||
Ordinary responses keep their existing complete result contract.
|
||||
|
||||
### Error recovery and deadline ownership
|
||||
|
||||
Initialization instructions and the static JavaScript tool description teach
|
||||
recovery before a host can discard a tool response. Invalid input calls should be corrected from the connected schema
|
||||
(refresh `tools/list` only if stale), not retried unchanged. REA's returned
|
||||
`provider_timeout` describes its server/provider deadline; provider-authored
|
||||
remediation names the applicable controls and preserves partial Evidence and
|
||||
cleanup uncertainty.
|
||||
|
||||
A host timeout without a REA result is different: execution and cleanup remain
|
||||
unknown. Increasing an advertised `analysis_timeout_ms` cannot fix a shorter
|
||||
client deadline. Configure the client's tool timeout with execution, cleanup
|
||||
and delivery headroom, or report that change is needed when not authorized.
|
||||
Session status does not establish that all target-free work stopped. Detailed
|
||||
host settings and bounded retry guidance live in the shipped skill's
|
||||
[recovery reference](https://github.com/morluto/rea/blob/main/.agents/skills/reverse-engineer-anything/references/connection-and-recovery.md#distinguish-client-and-server-deadlines).
|
||||
|
||||
### Untrusted target content
|
||||
|
||||
Strings, symbols, decompiled code and pseudocode, page text, script bodies,
|
||||
@@ -510,6 +528,17 @@ concrete version, selection source, and complete analysis profile through
|
||||
choices return typed selection details. A selected provider is never replaced
|
||||
automatically after a runtime failure.
|
||||
|
||||
`open_bundle_resources` selects a separate read-only app resource session
|
||||
without resolving `CFBundleExecutable`, including when it is absent or
|
||||
unresolvable. `binary_session` reports `session_kind: "bundle_resources"` and the
|
||||
caller and canonical resource-root paths; it does not report a binary target or
|
||||
deep-provider binding. `inspect_plist`, `decode_interface_builder`,
|
||||
`inspect_asset_catalog`, and `inspect_keyed_archive` retain resource-root
|
||||
provenance and use a null Evidence subject when executable identity is unknown.
|
||||
Binary-only operations and binary analysis snapshots do not apply in this
|
||||
session. `open_binary` can switch back to executable analysis, and
|
||||
`close_binary` retires either session kind.
|
||||
|
||||
Each provider client receives `binary_session.analysis_run.run_id` before any
|
||||
provider startup, including clients whose startup fails. `process_lineage` is
|
||||
`not_observed` until a dynamic provider starts, then becomes `snapshots` with
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
# Model file declarations
|
||||
|
||||
Use `inspect_model_artifact` (CLI: `inspect-model-artifact`) to inspect a local
|
||||
GGUF v2/v3 or safetensors file. Detection uses content, including GGUF byte
|
||||
order, rather than its extension. REA does not load a model, interpret weights,
|
||||
run inference, import Python, open external-data references or use the network.
|
||||
|
||||
Unsupported GGUF versions remain refused. Their diagnostic preserves the four
|
||||
version bytes and both UINT32 interpretations with byte order unknown, rather
|
||||
than inferring big-endian from an unrecognized little-endian value. Little-endian
|
||||
v2/v3 and big-endian v3 remain supported; big-endian v2 remains refused.
|
||||
|
||||
```bash
|
||||
rea inspect-model-artifact /absolute/path/model.gguf --json
|
||||
rea inspect-model-artifact /absolute/path/model.safetensors --hash full --json
|
||||
```
|
||||
|
||||
The CLI defaults to complete Evidence. For compact delivery with portable access
|
||||
to every retained observation:
|
||||
|
||||
```bash
|
||||
rea inspect-model-artifact /absolute/path/model.gguf \
|
||||
--detail summary --evidence_output /absolute/path/model-evidence.json --json
|
||||
```
|
||||
|
||||
## Identity and reads
|
||||
|
||||
The default `hash: "header"` reads only the metadata and tensor directory. The
|
||||
result records the exact selected path, file size, header length, header SHA-256,
|
||||
bytes read and read scope. GGUF alignment padding is outside this prefix.
|
||||
`artifact.sha256` and the Evidence subject are null: a header digest is not a
|
||||
commitment to the weights. Different files can have identical headers and sizes.
|
||||
|
||||
`hash: "full"` additionally streams the remaining file bytes through SHA-256.
|
||||
It returns a full-file digest and Evidence subject, while retaining the same
|
||||
header digest. It uses bounded buffers and does not interpret tensor data, but
|
||||
it does read the whole file. Cost therefore grows with file size. Both modes
|
||||
check descriptor and selected-path identity and metadata before completion;
|
||||
this detects observed replacement or modification, not adversarial modification
|
||||
that defeats filesystem metadata checks.
|
||||
|
||||
## Facts and selected queries
|
||||
|
||||
GGUF reports version, byte order, alignment, typed metadata, and each tensor's
|
||||
name, original type ID, dimensions, relative data offset, file offset and derived
|
||||
byte length. Alignment accepts every positive UINT32 multiple of eight, including
|
||||
non-powers of two. Metadata keys use ASCII lower-snake-case hierarchy components;
|
||||
standard numeric components such as `general.base_model.0.name` are preserved.
|
||||
Scalar metadata is complete. Arrays have declared element types,
|
||||
counts and at most eight preview elements per array, with an explicit
|
||||
`preview_complete` flag. This includes tokenizer vocabularies: omitted values
|
||||
are not silently represented as a complete array. Strings are UTF-8; 64-bit
|
||||
integer metadata and all shape dimensions use decimal strings without rounding.
|
||||
Nonfinite floating metadata uses `NaN`, `+Infinity` or `-Infinity` with its
|
||||
declared float type; negative zero is `"-0"`. Unknown tensor types retain their numeric IDs and null byte
|
||||
lengths; affected totals and storage validation remain unknown.
|
||||
|
||||
Safetensors reports the header extent, exact string `__metadata__` values, dtype,
|
||||
shape and declared data ranges. JSON duplicate keys, invalid UTF-8, out-of-file
|
||||
ranges, overlaps, gaps, trailing unindexed data and known dtype/shape byte-length
|
||||
contradictions are rejected with a typed `file_format_rejected` constraint (public code `target_unavailable`, category `unsupported_target`). Empty
|
||||
tensors and scalar shapes are supported; packed sub-byte types require whole
|
||||
byte storage. Unknown dtypes retain declared ranges and an explicit limitation
|
||||
that shape-to-storage validation is unavailable. Each tensor records
|
||||
`storage_validation: "consistent" | "unknown"`; `coverage.storage_sizes` remains
|
||||
partial when that consistency cannot be checked. Tensor filters accept these
|
||||
`storage_validation` states independently of dtype or name.
|
||||
|
||||
Parameter counts and per-type totals are **derived from declarations**, not
|
||||
measurements of effective parameters or inference behavior. Shared or repeated
|
||||
weights are not deduplicated. GGUF dimensions retain their original fastest-first
|
||||
order; safetensors shapes retain JSON order. Safetensors tensor rows use name
|
||||
order; GGUF rows use directory order. An index identifies a declaration within
|
||||
its collection and header digest, not tensor contents or a cross-version match.
|
||||
GGUF supports zero extents and scalar shapes, with at most four dimensions;
|
||||
known quantized types still require complete blocks in the first dimension.
|
||||
|
||||
MCP defaults to a compact summary with counts, selected short headline metadata,
|
||||
chat-template presence and derived totals. The full type histogram and all rows
|
||||
remain in complete Evidence. Headline metadata uses short keys/types and scalar
|
||||
values; long or compound values remain available through explicit metadata
|
||||
queries. Use the returned `parent_evidence_id`:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "<parent_evidence_id>"
|
||||
},
|
||||
"view": {
|
||||
"kind": "model-artifact",
|
||||
"collection": "tensors",
|
||||
"names": ["token_embd.weight", "output.weight"],
|
||||
"fields": ["name", "type", "shape", "file_offset", "byte_length"],
|
||||
"offset": 0,
|
||||
"limit": 32
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To answer a known question in the initial inspection, pass the same selection
|
||||
without `kind` as `inspect_model_artifact`'s optional `view`, for example
|
||||
`{"path":"/absolute/path/model.gguf","view":{"collection":"tensors","names":["token_embd.weight"],"fields":["type","shape","file_offset"]}}`.
|
||||
This returns one selected page with compact model context and retains the full
|
||||
source Evidence, avoiding another tool call. `view` requires summary delivery;
|
||||
complete detail and a selection are rejected as conflicting choices.
|
||||
CLI uses `--view '{"collection":"tensors","names":["token_embd.weight"]}'`
|
||||
with `--detail summary --evidence_output FILE`.
|
||||
|
||||
Call `inspect_analysis_view` with the historical query object above for further pages. `collection: "metadata"` accepts
|
||||
exact `keys`, indices, fields and paging; use it for `tokenizer.chat_template`
|
||||
or tokenizer array previews. `collection: "types"` pages derived storage and
|
||||
parameter totals and accepts exact `types`. Tensor pages also accept `types`.
|
||||
Filters intersect; omitted filters mean all recorded rows and empty arrays mean
|
||||
none. Every row retains its source-scoped index. Names, keys and type labels
|
||||
follow field selection; `fields: []` returns only indices. Omitted fields return
|
||||
all recorded columns. Compact warnings use the shared bounded view budget and
|
||||
link to complete parent Evidence when messages are omitted. Page offsets count filtered
|
||||
matches, while row indices retain their original scope. Each page carries source
|
||||
identity, header coverage, array omissions, unknowns and parent Evidence links.
|
||||
|
||||
Pages read historical retained facts without reopening the model. Complete
|
||||
Evidence can also be supplied inline or as a single authenticated Evidence JSON
|
||||
record through `source: {"kind": "evidence-file", "path": "/absolute/path/model-record.json"}`.
|
||||
CLI Evidence bundles contain a `records` array; import the bundle with
|
||||
`import_evidence_bundle` before using retained queries, or save one complete
|
||||
record as a standalone Evidence file. A bundle is not a single Evidence record.
|
||||
The session owns validated indexes until that Evidence is released; there is no
|
||||
live-file result cache or automatic freshness claim.
|
||||
Imported reports must retain positional indices and unique names, keys and
|
||||
type identities; malformed reports return a typed input error before indexing.
|
||||
|
||||
## Bounds and unsupported inputs
|
||||
|
||||
The reader admits one active inspection per provider. It enforces a 100,000,000
|
||||
byte header ceiling (safetensors counts its declared JSON bytes separately from
|
||||
the fixed eight-byte length prefix; GGUF counts the complete header) and a separate 32 MiB
|
||||
retained representation budget before retaining rows or expanding strings.
|
||||
Numeric products use exact integer arithmetic. GGUF follows its four-dimensional
|
||||
tensor layout and limits metadata array nesting to 32 frames. Resource refusals
|
||||
are typed, rather than returned as complete empty inventories. Cancellation
|
||||
joins descriptor finalization; failed cleanup remains owned for retry.
|
||||
|
||||
PyTorch pickle and ZIP model deserialization are unsupported; convert using a
|
||||
trusted external workflow and supply GGUF or safetensors. ONNX, TFLite, Core ML,
|
||||
model execution and conversion are outside this operation.
|
||||
|
||||
Format references: [GGUF specification](https://github.com/ggml-org/ggml/blob/da8e2a54b919557b816b1c2beb432ccb766f7ce0/docs/gguf.md),
|
||||
[ggml tensor layouts](https://github.com/ggml-org/ggml/blob/da8e2a54b919557b816b1c2beb432ccb766f7ce0/src/ggml-common.h),
|
||||
[safetensors format](https://github.com/huggingface/safetensors/tree/e246a2560645b7525f5775669ed816eb57c5bcc8#format).
|
||||
@@ -298,13 +298,20 @@ markup, including UTF-8 and BOM-marked UTF-16 sources. Unidentified text and
|
||||
OpenStep plists, including a data-only value such as `<deadbeef>`, report
|
||||
`unknown` even when plutil decodes their values through an XML conversion.
|
||||
|
||||
Binary plist inspection reads the original bytes through an owned file descriptor,
|
||||
so conversion cannot erase control characters, fractional dates, negative zero,
|
||||
or the high word of a 128-bit integer. It accepts at most 64 MiB of input and
|
||||
reserves an estimated 64 MiB decode budget before parsing; this estimate is not
|
||||
a hard process-memory bound. Binary-source provenance records the `file`
|
||||
classification without claiming a `plutil` conversion.
|
||||
|
||||
A plist explicitly selected through the path input or as the active plist target
|
||||
reports an unreadable or malformed selection as `invalid_request`. A missing or
|
||||
unreadable default plist of another target reports `capability_unavailable`.
|
||||
Permission failures remain `access_denied`; output and cleanup failures retain
|
||||
their own error categories.
|
||||
|
||||
A plist decode refusal includes plutil's diagnostic from either output stream.
|
||||
A conversion refusal includes plutil's diagnostic from either output stream.
|
||||
`details.captured_output` retains bounded stdout/stderr excerpts, observed byte
|
||||
totals and exit status. `truncated: true` means those excerpts are incomplete.
|
||||
|
||||
@@ -425,10 +432,32 @@ complete observation.
|
||||
vtable descriptors. Records include exact virtual addresses/file offsets and
|
||||
artifact evidence. Vtable indexes are metadata word offsets; witness indexes
|
||||
start after the conformance header. Names unavailable in metadata remain null.
|
||||
The `swift_class_vtable_descriptors` coverage counters count examined vtable
|
||||
slots and successfully decoded slots, rather than type-section entries.
|
||||
The `objc_class_method_ivar_metadata` counters count admitted class and
|
||||
metaclass objects and successfully decoded objects.
|
||||
Pointers are decoded through the image's fixups:
|
||||
- `LC_DYLD_CHAINED_FIXUPS` rebases and binds, including the authenticated
|
||||
arm64e formats;
|
||||
- legacy `LC_DYLD_INFO` bind opcodes.
|
||||
- legacy `LC_DYLD_INFO` rebase and bind opcodes.
|
||||
|
||||
A Swift conformance whose type or protocol reference is an external bind
|
||||
keeps its name as `null` and carries the raw dyld identity in
|
||||
`type_binding` or `protocol_binding`; that record and its facet coverage are
|
||||
partial. Consumers of this output must handle nullable conformance names
|
||||
and use the typed binding fields instead of substituting a symbol as a
|
||||
display name. Indirect relative-pointer records also retain their binding
|
||||
identity. A decoded rebase to virtual address zero remains an address; only
|
||||
an unfixed zero in a nullable ABI field is treated as null. Incomplete or
|
||||
conflicting fixup streams keep affected pointers unresolved; coverage reports
|
||||
parsing and resource limits while retaining validated prefix facts.
|
||||
Conformance records preserve the raw three-bit `type_reference_kind`. Kinds
|
||||
0 and 1 identify direct and indirect nominal descriptors; kind 2 preserves
|
||||
the inline Objective-C class name and its source location. Kind 3 preserves
|
||||
an imported class binding or the local class-object address when available,
|
||||
but class metadata/name recovery requiring the Objective-C runtime remains
|
||||
partial. Unknown kinds retain the raw kind and reference location without
|
||||
being treated as absent or assigned a guessed type name.
|
||||
|
||||
So an external superclass resolves from its `_OBJC_CLASS_$_` bind (for
|
||||
example `NSObject`). The `pointer_fixups` coverage facet names the mechanism.
|
||||
@@ -466,6 +495,21 @@ complete observation.
|
||||
that operation is unavailable; runtime failures remain failures rather than
|
||||
being replaced by symbol-only results.
|
||||
|
||||
`get_objc_classes` keeps names from the provider inventory. It coalesces rows
|
||||
only when their exact selected-image pointer slots decode to the same class
|
||||
import, including its binding stream, raw library ordinal, resolved library, weak status, and addend. A literal filter is
|
||||
applied to the original names before that join; if the filter selects only an
|
||||
alias, the output keeps that exact row. `identity_coverage` distinguishes a
|
||||
complete selected-inventory scan from partial, unsupported, and unknown cases.
|
||||
Recognized Mach-O formats outside the native metadata reader's support retain
|
||||
the provider's class labels with unknown import identity; malformed headers and
|
||||
read failures remain errors.
|
||||
The join also requires matching whole-artifact digests and matching provider-
|
||||
owned selected-image facts: container, byte range, full raw CPU type/subtype,
|
||||
and FAT table digest when present. A missing or overridden loader selection
|
||||
remains unknown; the result describes agreement on the selected artifact image,
|
||||
not an atomic observation of a running process or whole-program class coverage.
|
||||
|
||||
## Native desktop observation
|
||||
|
||||
`observe_native_ui` captures one explicitly selected existing PID/window ID.
|
||||
@@ -684,7 +728,13 @@ leap seconds) and out-of-range dates remain explicit unknowns. REA does not
|
||||
emulate CoreFoundation's normalization of overflowing calendar fields. Invalid
|
||||
dates returned by other decoders retain `{ "$plist_type": "date", "iso8601": null }`
|
||||
with a counted limitation. `inspect_plist` and Interface Builder decoding use
|
||||
the same projection.
|
||||
the same projection. Binary plist decoding refuses a reachable date whose marker
|
||||
is not `0x33`; date and numeric payloads must end before the offset table.
|
||||
Valid overlapping object records retain their independent source values. A valid `0x33` date is
|
||||
reported with an ISO-8601 timestamp only when its float64 seconds are exactly
|
||||
the canonical encoding of that millisecond instant. Any other finite binary
|
||||
date uses `{ "$plist_type": "date", "iso8601": null }` and the counted
|
||||
limitation; the archive bytes remain the source of the instant.
|
||||
|
||||
A negative-zero real is reported as `{ "$plist_type": "real", "value": "-0" }`
|
||||
because a JSON number cannot carry the sign of zero. `inspect_plist` and
|
||||
|
||||
@@ -67,7 +67,11 @@ Addresses count once per packet, including loopback and nested IP occurrences.
|
||||
DNS addresses are associated only with one question in the same response packet;
|
||||
additional records may be included, so this does not prove final resolution.
|
||||
Ambiguous addresses remain in a separate inventory. Protocol distribution counts
|
||||
exact `frame.protocols` stack strings. TLS SNI is recorded handshake metadata,
|
||||
exact nonempty `frame.protocols` stack strings. Frames for which TShark reports
|
||||
an empty stack, including zero-length packets, still contribute to the packet
|
||||
count without adding a protocol row. An all-empty stack inventory can have
|
||||
complete examined coverage; it does not establish the absence of network traffic.
|
||||
TLS SNI is recorded handshake metadata,
|
||||
not process identity or proof of the destination's authenticity.
|
||||
|
||||
All examined inventories are retained within the resource budget; no fixed
|
||||
|
||||
+27
-1
@@ -461,7 +461,33 @@ selected roots; it is not a per-file allowance. File contents are read in
|
||||
|
||||
Ownership inspections bind run-token reads to native start identities and retain
|
||||
related generations through reparenting, group changes, omitted rows, and cleanup
|
||||
retries. The captured run keeps one evidence owner through settlement and cleanup.
|
||||
retries. A host without native identity reads still token-checks process-table
|
||||
rows that appear only in a launcher-exit refresh before reporting that group
|
||||
clean or publishing an empty lineage. A matching token authorizes a signal only
|
||||
for a group whose leader carries the same token. A matching token whose leader
|
||||
is absent, or an unreadable token for a still-live new row, stays unresolved. A
|
||||
readable foreign token is not signaled. An unreadable row that has exited by
|
||||
the confirmation read does not hide a new row in that confirmation table, and a
|
||||
group proven while revalidating another owned group is signaled in the same
|
||||
cleanup. A matched row that changes parent or group on the confirmation read
|
||||
is token-checked again. A PID whose token was already read is read again
|
||||
on the next confirmation even when its parent and group return to the
|
||||
pre-exit values. A PID whose first token was foreign is read again
|
||||
on that table, because the PID alone does not identify the process. A PID
|
||||
from the pre-exit table is read again when its parent or group changes, and
|
||||
an unchanged pre-existing row that was never token-read stays unread. Lineage
|
||||
publishes one row for each PID. Confirmation reads are bounded; a table that keeps producing unreadable
|
||||
replacements stays unresolved. That refresh does not retain a ledger
|
||||
for a later attempt that never observes the launcher. Lineage reads a row again
|
||||
when its parent, group, or launcher-tree membership changes, and it reads a
|
||||
token again on the next refresh even when those fields are unchanged. The
|
||||
launcher is read on every lineage refresh; a missing, unowned, or
|
||||
relation-changed launcher PID is not published as a verified lineage. A
|
||||
descendant absent from the latest refresh is omitted. An
|
||||
ancestor move cannot leave a foreign descendant inside a verified result. A
|
||||
group that reappears after cleanup already processed it is revalidated once;
|
||||
another appearance stays unresolved. The captured run keeps
|
||||
one evidence owner through settlement and cleanup.
|
||||
An absent or changed token leaves a previously related generation unresolved;
|
||||
current token proof is still required before signaling. Swept cleanup verifies the
|
||||
remaining family after its group signals. Coverage is bounded by the native
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
# Python packaged applications
|
||||
|
||||
REA statically inventories PyInstaller executables with an appended CArchive.
|
||||
`inspect_artifact` and `extract_artifact` share the same reader through CLI and
|
||||
MCP. They read archive bytes without launching the application. Bytecode
|
||||
source decompilation, encrypted PYZ recovery, zipapp and PEX analysis are outside
|
||||
this capability.
|
||||
|
||||
## Facts and focused queries
|
||||
|
||||
The root occurrence's `format_metadata` records the CArchive layout (2.0 or
|
||||
2.1), archive and cookie offsets, table extent, entry count, raw Python version,
|
||||
normalized major/minor when known, library name and encryption marker. Member
|
||||
metadata retains the original name, table index, type, compression flag,
|
||||
relative offset and known directory record extent. Logical paths use archive
|
||||
path rules; the source name retains its spelling, including backslashes.
|
||||
`relative_offset` is relative to the CArchive start for CArchive rows, or to
|
||||
the decoded PYZ image for PYZ rows. A CArchive directory record starts at
|
||||
`archive_start + toc_offset + directory_record_offset`; its size is
|
||||
`directory_record_size`. PYZ row directory extents remain unknown.
|
||||
|
||||
Namespace packages remain directories. Their `relative_offset` and
|
||||
`compressed_size` preserve any payload declared by older producers, while
|
||||
`declared_size` is zero because no bytecode is materialized for a namespace.
|
||||
Every declared span must end before the PYZ table of contents.
|
||||
PYZ `compression_flag` is derived from storage: nonempty payloads are compressed,
|
||||
including legacy namespaces; an empty namespace has no compressed payload.
|
||||
|
||||
CArchive type `o` entries are runtime options. They are metadata occurrences,
|
||||
with no content artifact or extractable file. Their query paths are
|
||||
`__pyinstaller__/runtime-options/<table index>`; the option text is
|
||||
`format_metadata.source_name`. Repeated declarations retain distinct occurrence
|
||||
identities. These paths do not claim output filesystem names.
|
||||
|
||||
Type `d` entries are multipackage dependencies. They are also metadata, under
|
||||
`__pyinstaller__/dependencies/<table index>`, and never materialize an empty file.
|
||||
The reader preserves the raw reference and splits its first colon into
|
||||
`dependency_archive` and `dependency_member`. `dependency_status` remains
|
||||
`unresolved`, or `invalid-reference` with unknown parts for malformed references.
|
||||
`external_dependency_count` summarizes these declarations. REA does not open
|
||||
another executable or claim its contents are available. A real file with the
|
||||
same logical path as metadata remains independently extractable.
|
||||
Both declaration types require empty payloads and no compression. Their query
|
||||
paths do not participate in filesystem collision checks, directory ancestry or
|
||||
source-map target lookup; their parent identifies the declaring container.
|
||||
Complete authenticated inventory page sets preserve producer identities in any
|
||||
merge order, including metadata/file aliases. Malformed TOC diagnostics name the
|
||||
table index; original names remain available in the supplied artifact.
|
||||
Path failures use bounded escaped previews and report the original length;
|
||||
accepted paths and retained source names are not shortened.
|
||||
|
||||
Request `detail: "summary"` for a compact inspection, then query retained
|
||||
Evidence with `inspect_analysis_view`. An inventory view can select
|
||||
`collection: "occurrences"`, `entry_kinds: ["metadata"]`, exact logical paths or
|
||||
occurrence IDs, requested `fields`, and `offset`/`limit`. Filters intersect;
|
||||
identities, parent Evidence and coverage survive projection. Full inventory
|
||||
Evidence remains available for export. A compact reply reduces returned context;
|
||||
it still performs the complete member inventory and hashing work.
|
||||
Focused views add calls and tool-schema costs. A complete small inventory can
|
||||
cost less overall; prefer compact delivery when the full member graph would
|
||||
overwhelm the answer.
|
||||
|
||||
```bash
|
||||
rea inspect-artifact /absolute/path/hello --detail summary --evidence_output /absolute/new/evidence.json --json
|
||||
rea extract-artifact /absolute/path/hello --json
|
||||
```
|
||||
|
||||
## Decoding and evidence limits
|
||||
|
||||
Inventory hashes members individually through bounded streams. Stored PYZ
|
||||
images use descriptor ranges; outer-compressed images stream to private files,
|
||||
which the reader owns through cleanup and any cleanup retry. Extraction binds
|
||||
the selected carrier to its inventoried digest and checks source identity and
|
||||
state before and after reading. It does not retain expanded module bodies.
|
||||
Opaque outer-compressed images reuse that verified private image when opened,
|
||||
instead of inflating the carrier again. Direct source spans describe stored
|
||||
CArchive bytes; decompressed content has no direct span even when its encoded
|
||||
and decoded sizes coincide. Original offsets and compression remain metadata.
|
||||
|
||||
Only the PYZ header and marshal directory are loaded for listing. PYZ tables
|
||||
have a 16 MiB budget; CArchive tables have a 32 MiB budget. Parsed facts have a
|
||||
32 MiB serialized budget. Projected entries share a 64 MiB retained budget,
|
||||
including metadata, bookkeeping and conservative reservations for namespace
|
||||
objects and possible case-collision annotations at each path component. These
|
||||
reservations happen before normalization or trie allocation.
|
||||
Outer-compressed PYZ images have a separate per-reader capacity of 128 private
|
||||
files, bounding retained inodes and image identities. The complete image count
|
||||
is admitted before any temporary file is created; excess images return a
|
||||
capacity failure. This is a resource policy, not a PyInstaller format limit.
|
||||
Only a zero-byte read establishes EOF; short successful descriptor reads fill
|
||||
the bounded range. Compressed members must consume their complete declared
|
||||
input span; trailing junk and concatenated streams are refused.
|
||||
Plaintext expansion has an aggregate archive-derived budget. Exceeding a budget
|
||||
returns a resource failure.
|
||||
|
||||
Supported final Python bytecode magic values cover Python 3.7 through 3.14.
|
||||
Supported marshal directory values are lists, tuples, strings, integers and
|
||||
bytes. An unsupported magic or directory stays an opaque member with a
|
||||
limitation and `pyz_status: "opaque"`; its module contents remain unknown.
|
||||
Any opaque PYZ makes producer traversal coverage unknown, including retained
|
||||
summaries and exhausted queries. Per-archive reasons remain on the opaque
|
||||
occurrences; the coverage observation summarizes their count without repeating
|
||||
every limitation. An empty result does not establish module absence.
|
||||
Observed `pyz_python_magic` bytes remain available even when their Python version
|
||||
is unknown.
|
||||
The directory declares compressed module lengths, so uncompressed declared
|
||||
sizes and source spans inside decoded data remain unknown.
|
||||
|
||||
PYZ modules receive a derived 16-byte `.pyc` header from their own supported
|
||||
magic. CArchive bytecode receives that header only when every sibling PYZ magic
|
||||
and the cookie's Python version agree. Otherwise it retains its original bytes
|
||||
and `bytecode_header: "unknown"`. `bytecode_magic` and graph derivation edges
|
||||
identify synthesized headers; member digests cover the resulting derived files.
|
||||
These headers do not establish successful execution or decompilation.
|
||||
|
||||
The nonempty CArchive Python module `pyimod00_crypto_key` (`m`/`M`) is recorded
|
||||
as an encryption marker; a same-named data file or script is not. Affected
|
||||
PYZ blobs are reported without decoding; extraction refuses encrypted entries.
|
||||
The raw PYZ container remains readable and hashable, including when its module
|
||||
table is opaque. Its marker stays in metadata and opaque child coverage stays
|
||||
unknown; extracting the container does not decrypt its payloads.
|
||||
The marker also applies to nonempty legacy namespace payloads. Empty namespaces
|
||||
have no payload to encrypt and retain a null encryption source.
|
||||
|
||||
Detection searches the last 16 MiB for a cookie, allowing a trailing macOS code
|
||||
signature within that bound. Member offsets are relative to the archive start:
|
||||
cookie position plus cookie size minus package length. Native bootloader
|
||||
analysis remains available through the native tools.
|
||||
@@ -152,6 +152,7 @@ npx rea-agents@latest setup
|
||||
| تسجيلات الشبكة المحفوظة | الطلبات، والاستجابات، ومحتويات الحمولة المتاحة، ومواضعها في المصدر | HAR؛ وmitmdump على Linux لتسجيلات mitmproxy بصيغتها الأصلية؛ [دليل تسجيلات الشبكة](../web-network-captures.md) |
|
||||
| التقاط الحزم | نقاط النهاية وDNS وTLS SNI وتوزيع البروتوكولات وطلبات HTTP | tshark وcapinfos يوفّرهما المُستدعي؛ [دليل pcap](../pcap-analysis.md) |
|
||||
| تجميعات .NET | البيانات الوصفية، وتعليمات CIL، واعتماديات الشيفرة الأصلية المعلنة، ومقارنات البناء | فحص ثابت؛ [دليل الشيفرة المُدارة](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| حزم Android APK | تصريحات ملف manifest، والأصناف، والدوال الناتجة عن فك الترجمة، والمراجع | 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) |
|
||||
@@ -266,6 +267,7 @@ npx rea-agents@latest setup
|
||||
|
||||
- [التثبيت والإعداد](../installation.md): تسجيل الوكلاء، وتهيئة الموفّرين، والتحديثات، وإلغاء التثبيت.
|
||||
- [الجاهزية واستكشاف الأخطاء](../installation.md#check-readiness-for-your-task): تشخيص وكيل أو محرك تحليل محدد.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI وEvidence](../cli.md): الأوامر، واختيار الموفّر، واللقطات، والاستيراد/التصدير، وحالات الخروج.
|
||||
- [عقود MCP](../mcp-contracts.md) و[تعليمات الوكلاء](../mcp-prompts.md): نتائج الأدوات، والجلسات، والاستقصاءات الموجّهة.
|
||||
- [كتالوج الأدوات](../mcp-contracts.md#generated-catalog): قائمة الأدوات والموفّرين وأوامر CLI المولّدة أثناء البناء.
|
||||
|
||||
@@ -152,6 +152,7 @@ REA benötigt Node.js 22.x (>=22.19), 24.x (>=24.11) oder 26+ sowie npm. Zusätz
|
||||
| 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) |
|
||||
| Paketerfassungen | Endpunkte, DNS, TLS SNI, Protokollverteilung und HTTP-Anfragen | Vom Aufrufer bereitgestellte tshark- und capinfos-Binaries; [pcap-Leitfaden](../pcap-analysis.md) |
|
||||
| .NET-Assemblies | Metadaten, CIL-Anweisungen, deklarierte native Abhängigkeiten und Build-Vergleiche | Statische Untersuchung; [Leitfaden für verwalteten Code](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| Android-APKs | Manifest-Deklarationen, Klassen, dekompilierte Methoden und Verweise | JADX ohne grafische Oberfläche und ein vollständiges JDK unter Linux/macOS/Windows x64; [Android-Leitfaden](../android-analysis.md) |
|
||||
| Android-Geräte | Geräte, Pakete, Prozesse, Logs, Dumps, Bildschirmaufnahmen sowie APK- und Dateiübertragungen | Vom Aufrufer bereitgestelltes adb (Emulator oder Gerät); [ADB-Leitfaden](../adb-device-analysis.md) |
|
||||
| JEB-Projekte | Engine-gestützte Projekteinheiten, Artefakt-Prüfsummen und Pseudokode von Typen/Methoden | Ein vom Aufrufer gestarteter JEB-Client mit MCP, ausgewählt über REA_JEB_MCP_URL; [JEB-Leitfaden](../jeb-analysis.md) |
|
||||
@@ -264,6 +265,7 @@ Beginne mit den [praxisnahen Anleitungen](https://rea.tools/de/guides/) auf der
|
||||
|
||||
- [Installation und Einrichtung](../installation.md): Agentenregistrierung, Provider-Konfiguration, Updates und Deinstallation.
|
||||
- [Bereitschaft und Fehlerbehebung](../installation.md#check-readiness-for-your-task): Diagnose eines bestimmten Agenten oder einer Analyse-Engine.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI und Evidence](../cli.md): Befehle, Provider-Auswahl, Snapshots, Import/Export und Exit-Status.
|
||||
- [MCP-Kontrakte](../mcp-contracts.md) und [Agenten-Prompts](../mcp-prompts.md): Werkzeugergebnisse, Sitzungen und geführte Untersuchungen.
|
||||
- [Werkzeugkatalog](../mcp-contracts.md#generated-catalog): Beim Build erzeugtes Inventar von Werkzeugen, Providern und CLI-Befehlen.
|
||||
|
||||
@@ -158,6 +158,7 @@ npx rea-agents@latest setup
|
||||
| Ιστότοποι | Δομή σελίδων, scripts, παρατηρήσεις δικτύου και ζητούμενα στιγμιότυπα οθόνης | Πρόγραμμα περιήγησης της οικογένειας Chrome· [ανάλυση μέσω προγράμματος περιήγησης](../browser-observation.md) |
|
||||
| Αποθηκευμένες καταγραφές δικτύου | Αιτήματα, αποκρίσεις, διαθέσιμα δεδομένα φορτίου και θέσεις στην πηγή | HAR ή SAZ του Fiddler Everywhere· mitmdump σε Linux για την εγγενή μορφή mitmproxy· [οδηγός καταγραφών](../web-network-captures.md) |
|
||||
| Assemblies .NET | Μεταδεδομένα, εντολές CIL, δηλωμένες εξαρτήσεις από κώδικα μηχανής και συγκρίσεις builds | Καμία πρόσθετη απαίτηση (μόνο στατική ανάλυση)· [οδηγός διαχειριζόμενου κώδικα](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| APK Android | Δηλώσεις manifest, κλάσεις, απομεταγλωττισμένες μέθοδοι και αναφορές | JADX χωρίς γραφικό περιβάλλον και πλήρες JDK σε Linux/macOS/Windows x64· [οδηγός Android](../android-analysis.md) |
|
||||
| Συσκευές Android | Συσκευές, πακέτα, διεργασίες, logs, dumps, στιγμιότυπα οθόνης και μεταφορές APK/αρχείων | adb που παρέχει ο χρήστης (εξομοιωτής ή φυσική συσκευή)· [οδηγός ADB](../adb-device-analysis.md) |
|
||||
| Έργα JEB | Μονάδες έργου από τη μηχανή ανάλυσης, ψηφιακά αποτυπώματα αρχείων και ψευδοκώδικα τύπων/μεθόδων | Client JEB που έχει ξεκινήσει ο χρήστης και παρέχει MCP, επιλεγμένος μέσω REA_JEB_MCP_URL· [οδηγός JEB](../jeb-analysis.md) |
|
||||
@@ -277,6 +278,7 @@ npx rea-agents@latest setup
|
||||
|
||||
- [Εγκατάσταση και ρύθμιση](../installation.md): καταχώριση agents, ρύθμιση παρόχων ανάλυσης, ενημερώσεις και απεγκατάσταση.
|
||||
- [Έλεγχος ετοιμότητας και αντιμετώπιση προβλημάτων](../installation.md#check-readiness-for-your-task): διάγνωση ενός agent ή μιας μηχανής ανάλυσης.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI και Evidence](../cli.md): εντολές, επιλογή παρόχου, στιγμιότυπα, εισαγωγή/εξαγωγή και κωδικοί εξόδου.
|
||||
- [Προδιαγραφές MCP](../mcp-contracts.md) και [prompts για agents](../mcp-prompts.md): αποτελέσματα εργαλείων, συνεδρίες και καθοδηγούμενες διερευνήσεις.
|
||||
- [Κατάλογος εργαλείων](../mcp-contracts.md#generated-catalog): αυτόματα παραγόμενος κατάλογος εργαλείων, παρόχων και εντολών CLI.
|
||||
|
||||
@@ -152,6 +152,7 @@ REA requiere Node.js 22.x (>=22.19), 24.x (>=24.11) o 26+, además de npm. Las h
|
||||
| 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) |
|
||||
| Capturas de paquetes | Endpoints, DNS, TLS SNI, distribución de protocolos y solicitudes HTTP | tshark y capinfos suministrados por el autor de la llamada; [guía pcap](../pcap-analysis.md) |
|
||||
| Ensamblados .NET | Metadatos, instrucciones CIL, dependencias nativas declaradas y comparaciones de compilaciones | Inspección estática; [guía de código administrado](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| APK de Android | Declaraciones del manifiesto, clases, métodos descompilados y referencias | JADX sin interfaz gráfica y un JDK completo en Linux/macOS/Windows x64; [guía de Android](../android-analysis.md) |
|
||||
| Dispositivos Android | Dispositivos, paquetes, procesos, registros, volcados, capturas de pantalla y transferencias de APK/archivos | adb suministrado por el autor de la llamada (emulador o dispositivo); [guía de ADB](../adb-device-analysis.md) |
|
||||
| Proyectos JEB | Unidades de proyecto del motor, resúmenes de artefactos y pseudocódigo de tipos/métodos | Un cliente JEB iniciado por el autor de la llamada que sirve MCP, seleccionado con REA_JEB_MCP_URL; [guía de JEB](../jeb-analysis.md) |
|
||||
@@ -264,6 +265,7 @@ Empieza con las [guías prácticas](https://rea.tools/es/guides/) del sitio web.
|
||||
|
||||
- [Instalación y configuración](../installation.md): registro de agentes, configuración de proveedores, actualizaciones y desinstalación.
|
||||
- [Preparación y resolución de problemas](../installation.md#check-readiness-for-your-task): diagnóstico de un agente o motor de análisis concreto.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI y Evidence](../cli.md): comandos, selección de proveedores, instantáneas, importación/exportación y estados de salida.
|
||||
- [Contratos de MCP](../mcp-contracts.md) y [prompts para agentes](../mcp-prompts.md): resultados de herramientas, sesiones e investigaciones guiadas.
|
||||
- [Catálogo de herramientas](../mcp-contracts.md#generated-catalog): inventario de herramientas, proveedores y comandos de CLI generado durante la compilación.
|
||||
|
||||
@@ -143,6 +143,7 @@ REA به Node.js 22.x (>=22.19)، 24.x (>=24.11) یا 26+، بههمراه np
|
||||
| وبسایتها | ساختار صفحه، اسکریپتها، مشاهدات شبکه و اسکرینشاتهای درخواستی | مرورگر مبتنی بر Chrome؛ [تحلیل مرورگر](../browser-observation.md) |
|
||||
| دادههای ضبطشدهٔ شبکه | درخواستها، پاسخها، payloadهای آشکارشده و مکان آنها در منبع | HAR؛ ابزار mitmdump در لینوکس برای ضبطهای بومی mitmproxy؛ [راهنمای ضبط شبکه](../web-network-captures.md) |
|
||||
| اسمبلیهای .NET | فراداده، دستورالعملهای CIL، وابستگیهای بومی اعلامشده و مقایسهٔ buildها | بررسی ایستا؛ [راهنمای کد مدیریتشده](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| فایلهای APK اندروید | تعاریف Manifest، کلاسها، متدهای دیکامپایلشده و ارجاعات | JADX بدون رابط گرافیکی و JDK کامل در Linux/macOS/Windows x64؛ [راهنمای اندروید](../android-analysis.md) |
|
||||
| دستگاههای اندروید | دستگاهها، بستهها، فرایندها، لاگها، دامپها، اسکرینشاتها و انتقال APK/فایل | adb تأمینشده توسط فراخوانکننده (شبیهساز یا دستگاه)؛ [راهنمای ADB](../adb-device-analysis.md) |
|
||||
| پروژههای JEB | واحدهای پروژه از موتور، هشهای مصنوع و شبهکدِ نوع/متد | یک کلاینت JEB که توسط فراخوانکننده اجرا شده و MCP ارائه میدهد و با REA_JEB_MCP_URL انتخاب میشود؛ [راهنمای JEB](../jeb-analysis.md) |
|
||||
@@ -249,6 +250,7 @@ npx rea-agents@latest setup
|
||||
|
||||
- [نصب و راهاندازی](../installation.md): ثبت ایجنت، پیکربندی ارائهدهنده، بهروزرسانی و حذف نصب.
|
||||
- [آمادگی و رفع اشکال](../installation.md#check-readiness-for-your-task): عیبیابی یک ایجنت یا موتور تحلیل.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI و شواهد](../cli.md): دستورات، انتخاب ارائهدهنده، snapshotها، ورود/خروج داده و وضعیتهای خروج.
|
||||
- [قراردادهای MCP](../mcp-contracts.md) و [پرامپتهای ایجنت](../mcp-prompts.md): نتایج ابزارها، نشستها و بررسیهای هدایتشده.
|
||||
- [فهرست ابزارها](../mcp-contracts.md#generated-catalog): فهرست تولیدشده هنگام build از ابزارها، ارائهدهندگان و دستورات CLI.
|
||||
|
||||
@@ -156,6 +156,7 @@ REA nécessite Node.js 22.x (>=22.19), 24.x (>=24.11) ou 26+, ainsi que npm. Les
|
||||
| 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) |
|
||||
| Captures de paquets | Points de terminaison, DNS, TLS SNI, distribution des protocoles et requêtes HTTP | tshark et capinfos fournis par l'appelant ; [guide pcap](../pcap-analysis.md) |
|
||||
| Assemblys .NET | Métadonnées, instructions CIL, dépendances natives déclarées et comparaisons de builds | Inspection statique ; [guide du code managé](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| APK Android | Déclarations du manifeste, classes, méthodes décompilées et références | JADX headless et un JDK complet sous Linux/macOS/Windows x64 ; [guide Android](../android-analysis.md) |
|
||||
| Appareils Android | Appareils, paquets, processus, journaux, dumps, captures d'écran et transferts d'APK/fichiers | adb fourni par l'appelant (émulateur ou appareil) ; [guide ADB](../adb-device-analysis.md) |
|
||||
| Projets JEB | Unités de projet fournies par le moteur, condensés d'artefacts et pseudo-code de types/méthodes | Un client JEB démarré par l'appelant servant MCP, sélectionné via REA_JEB_MCP_URL ; [guide JEB](../jeb-analysis.md) |
|
||||
@@ -268,6 +269,7 @@ Commencez par les [guides pas à pas](https://rea.tools/fr/guides/) du site web.
|
||||
|
||||
- [Installation et configuration](../installation.md) : enregistrement des agents, configuration des fournisseurs, mises à jour et désinstallation.
|
||||
- [Vérification et dépannage](../installation.md#check-readiness-for-your-task) : diagnostiquer un agent ou un moteur d'analyse donné.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI et Evidence](../cli.md) : commandes, choix du fournisseur, snapshots, import/export et codes de sortie.
|
||||
- [Contrats MCP](../mcp-contracts.md) et [prompts pour agents](../mcp-prompts.md) : résultats des outils, sessions et investigations guidées.
|
||||
- [Catalogue des outils](../mcp-contracts.md#generated-catalog) : inventaire des outils, fournisseurs et commandes CLI, généré lors du build.
|
||||
|
||||
@@ -159,6 +159,7 @@ Alat tambahan dan dukungan sistem host bergantung pada target yang dianalisis:
|
||||
| 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) |
|
||||
| Capturan paket | Endpoint, DNS, TLS SNI, distribusi protokol, dan permintaan HTTP | tshark dan capinfos buatan pemanggil; [panduan pcap](../pcap-analysis.md) |
|
||||
| Assembly .NET | Metadata, instruksi CIL, dependensi native yang dideklarasikan, dan perbandingan build | Tidak ada (hanya analisis statis); [panduan kode terkelola](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| APK Android | Deklarasi manifest, kelas, metode hasil dekompilasi, dan referensi | JADX mode headless dan JDK lengkap di Linux/macOS/Windows x64; [panduan Android](../android-analysis.md) |
|
||||
| Perangkat Android | Perangkat, paket, proses, log, dump, tangkapan layar, serta transfer APK/berkas | adb buatan pemanggil (emulator atau perangkat); [panduan ADB](../adb-device-analysis.md) |
|
||||
| Proyek JEB | Unit proyek bertenaga mesin, hash artefak, dan pseudokode tipe/metode | Klien JEB yang dijalankan pemanggil dan menyajikan MCP, dipilih dengan REA_JEB_MCP_URL; [panduan JEB](../jeb-analysis.md) |
|
||||
@@ -289,6 +290,7 @@ Untuk mengetahui opsi, prasyarat, dan kontrak hasil secara tepat:
|
||||
|
||||
- [Instalasi dan penyiapan](../installation.md): pendaftaran agen, konfigurasi penyedia, pembaruan, dan penghapusan instalasi.
|
||||
- [Kesiapan dan pemecahan masalah](../installation.md#check-readiness-for-your-task): mendiagnosis satu agen atau mesin analisis.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI dan Bukti](../cli.md): perintah, pemilihan penyedia, snapshot, impor/ekspor, dan status keluar.
|
||||
- [Kontrak MCP](../mcp-contracts.md) dan [prompt agen](../mcp-prompts.md): hasil alat, sesi, dan investigasi terpandu.
|
||||
- [Katalog alat](../mcp-contracts.md#generated-catalog): inventaris alat, penyedia, dan perintah CLI yang dibuat secara otomatis.
|
||||
|
||||
@@ -156,6 +156,7 @@ REA richiede Node.js 22.x (>=22.19), 24.x (>=24.11) o 26+, oltre a npm. Strument
|
||||
| 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) |
|
||||
| Captured di pacchetti | Endpoint, DNS, TLS SNI, distribuzione dei protocolli e richieste HTTP | tshark e capinfos forniti dal chiamante; [guida pcap](../pcap-analysis.md) |
|
||||
| Assembly .NET | Metadati, istruzioni CIL, dipendenze native dichiarate e confronti di build | Nessuno (solo statico); [guida managed-code](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| APK Android | Dichiarazioni manifest, classi, metodi decompilati e riferimenti | JADX headless e un JDK completo su Linux/macOS/Windows x64; [guida Android](../android-analysis.md) |
|
||||
| Dispositivi Android | Dispositivi, pacchetti, processi, log, dump, screenshot e trasferimenti APK/file | adb fornito dal chiamante (emulatore o hardware); [guida ADB](../adb-device-analysis.md) |
|
||||
| Progetti JEB | Unità di progetto dal motore, digest degli artifact e pseudocodice di tipi/metodi | Client JEB avviato dal chiamante via MCP, selezionato con REA_JEB_MCP_URL; [guida JEB](../jeb-analysis.md) |
|
||||
@@ -268,6 +269,7 @@ Inizia dalle [guide pratiche](https://rea.tools/it/guides/) del sito web. Per op
|
||||
|
||||
- [Installazione e configurazione](../installation.md): registrazione agente, configurazione provider, aggiornamenti e disinstallazione.
|
||||
- [Pronto all'uso e risoluzione dei problemi](../installation.md#check-readiness-for-your-task): diagnostica un agente o un motore di analisi.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI ed Evidence](../cli.md): comandi, selezione provider, snapshot, import/export e stati di uscita.
|
||||
- [Contratti MCP](../mcp-contracts.md) e [prompt agente](../mcp-prompts.md): risultati degli strumenti, sessioni e indagini guidate.
|
||||
- [Catalogo strumenti](../mcp-contracts.md#generated-catalog): inventario generato di strumenti, provider e comandi CLI.
|
||||
|
||||
@@ -152,6 +152,7 @@ REA には Node.js 22.x(>=22.19)、24.x(>=24.11)、または 26+ と npm
|
||||
| 保存済みのネットワークキャプチャ | リクエスト、レスポンス、取得可能なペイロード、ソース内の位置 | HAR;mitmproxy ネイティブ形式のキャプチャには Linux 上の mitmdump;[キャプチャガイド](../web-network-captures.md) |
|
||||
| パケットキャプチャ | エンドポイント、DNS、TLS SNI、プロトコル分布、HTTPリクエスト | 呼び出し側が用意した tshark と capinfos;[pcapガイド](../pcap-analysis.md) |
|
||||
| .NET アセンブリ | メタデータ、CIL 命令、宣言されたネイティブ依存関係、ビルドの比較 | 静的な検査;[マネージドコードのガイド](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.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) |
|
||||
@@ -266,6 +267,7 @@ npx rea-agents@latest setup
|
||||
|
||||
- [インストールとセットアップ](../installation.md):エージェントの登録、プロバイダーの設定、更新、アンインストール。
|
||||
- [準備状況の確認とトラブルシューティング](../installation.md#check-readiness-for-your-task):特定のエージェントや分析エンジンを診断。
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI と Evidence](../cli.md):コマンド、プロバイダーの選択、スナップショット、インポート/エクスポート、終了ステータス。
|
||||
- [MCP 契約](../mcp-contracts.md)と[エージェント向けプロンプト](../mcp-prompts.md):ツールの結果、セッション、ガイド付き調査。
|
||||
- [ツールカタログ](../mcp-contracts.md#generated-catalog):ビルド時に生成されるツール、プロバイダー、CLI コマンドの一覧。
|
||||
|
||||
@@ -173,6 +173,7 @@ REA에는 Node.js 22.x(>=22.19), 24.x(>=24.11) 또는 26+와 npm이 필요합니
|
||||
| 기록된 논리 신호 | 디코더 주석, 샘플 범위, 진단 정보 | 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) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
|
||||
정적 JavaScript 및 .NET 검사는 제공된 파일을 읽으며 애플리케이션을 실행하지 않습니다. 런타임 캡처는 사용자의 권한으로 선택한 대상을 실행하거나 대상과 상호작용합니다. 각 런타임 가이드에서 그 영향을 설명합니다.
|
||||
|
||||
@@ -279,6 +280,7 @@ npx rea-agents@latest setup
|
||||
|
||||
- [설치 및 설정](../installation.md): 에이전트 등록, 제공자 설정, 업데이트, 제거.
|
||||
- [준비 상태 확인 및 문제 해결](../installation.md#check-readiness-for-your-task): 특정 에이전트 또는 분석 엔진 진단.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI 및 Evidence](../cli.md): 명령, 제공자 선택, 스냅샷, 가져오기/내보내기, 종료 상태.
|
||||
- [MCP 계약](../mcp-contracts.md) 및 [에이전트 프롬프트](../mcp-prompts.md): 도구 결과, 세션, 안내에 따른 조사.
|
||||
- [도구 카탈로그](../mcp-contracts.md#generated-catalog): 빌드 시 생성되는 도구, 제공자, CLI 명령 목록.
|
||||
|
||||
@@ -160,6 +160,7 @@ REA-യ്ക്ക് Node.js 22.x (>=22.19), 24.x (>=24.11), അല്ലെ
|
||||
| Websites | Page structure, scripts, network observations, ആവശ്യപ്പെട്ട screenshots | Chrome-family browser; [browser analysis](../browser-observation.md) |
|
||||
| Saved network captures | Requests, responses, പുറത്തുകാണുന്ന payload-ുകൾ, source locations | HAR അല്ലെങ്കിൽ Fiddler Everywhere SAZ; native mitmproxy-നായി Linux-ൽ mitmdump; [capture guide](../web-network-captures.md) |
|
||||
| .NET assemblies | Metadata, CIL instructions, പ്രഖ്യാപിച്ച native dependencies, build comparisons | ഒന്നും ആവശ്യമില്ല (static മാത്രം); [managed-code guide](../managed-code-analysis.md) |
|
||||
| Python packaged apps | CArchive/PYZ entries, Python version, bytecode, resources and runtime options | Static PyInstaller parsing; [Python guide](../python-packaged-applications.md) |
|
||||
| Android APKs | Manifest declarations, classes, decompiled methods, references | Linux/macOS/Windows x64-ൽ Headless JADX-ഉം full JDK-ഉം; [Android guide](../android-analysis.md) |
|
||||
| Android devices | Devices, packages, processes, logs, dumps, screen captures, APK/file transfers | Caller നൽകുന്ന adb (emulator അല്ലെങ്കിൽ hardware); [ADB guide](../adb-device-analysis.md) |
|
||||
| JEB projects | Engine-backed project units, artifact digests, type/method pseudo-code | MCP സേവനം നൽകുന്ന caller ആരംഭിച്ച JEB client; REA_JEB_MCP_URL ഉപയോഗിച്ച് തിരഞ്ഞെടുക്കുക; [JEB guide](../jeb-analysis.md) |
|
||||
@@ -282,6 +283,7 @@ npx rea-agents@latest setup
|
||||
|
||||
- [Installation and setup](../installation.md): agent registration, provider configuration, updates, uninstall.
|
||||
- [Readiness and troubleshooting](../installation.md#check-readiness-for-your-task): ഒരു agent-ന്റെയോ analysis engine-ന്റെയോ പ്രശ്നങ്ങൾ കണ്ടെത്തുക.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI and Evidence](../cli.md): commands, provider തിരഞ്ഞെടുക്കൽ, snapshots, import/export, exit statuses.
|
||||
- [MCP contracts](../mcp-contracts.md), [agent prompts](../mcp-prompts.md): tool results, sessions, guided investigations.
|
||||
- [Tool catalog](../mcp-contracts.md#generated-catalog): tools, providers, CLI commands എന്നിവയുടെ സ്വയമേവ സൃഷ്ടിച്ച പട്ടിക.
|
||||
|
||||
@@ -152,6 +152,7 @@ REA wymaga Node.js 22.x (>=22.19), 24.x (>=24.11) lub 26+ oraz npm. Dodatkowe na
|
||||
| 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) |
|
||||
| Przechwytywanie pakietów | Punkty końcowe, DNS, TLS SNI, dystrybucja protokołów i żądania HTTP | Dostarczone przez wywołującego tshark i capinfos; [przewodnik pcap](../pcap-analysis.md) |
|
||||
| Zestawy .NET | Metadane, instrukcje CIL, zadeklarowane zależności natywne i porównania kompilacji | Analiza statyczna; [przewodnik kodu zarządzanego](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| Pakiety APK dla Androida | Deklaracje manifestu, klasy, zdekompilowane metody i odwołania | JADX bez interfejsu graficznego i pełny JDK w systemach Linux/macOS/Windows x64; [przewodnik Androida](../android-analysis.md) |
|
||||
| Urządzenia z Androidem | Urządzenia, pakiety, procesy, logi, zrzuty, zrzuty ekranu oraz transfery APK/plików | adb dostarczony przez wywołującego (emulator lub urządzenie); [przewodnik ADB](../adb-device-analysis.md) |
|
||||
| Projekty JEB | Jednostki projektu obsługiwane przez silnik, skróty artefaktów i pseudokod typów/metod | Uruchomiony przez wywołującego klient JEB udostępniający MCP, wybrany przez REA_JEB_MCP_URL; [przewodnik JEB](../jeb-analysis.md) |
|
||||
@@ -264,6 +265,7 @@ Zacznij od [przewodników z przykładami](https://rea.tools/pl/guides/) na stron
|
||||
|
||||
- [Instalacja i konfiguracja](../installation.md): rejestracja agentów, konfiguracja dostawców analizy, aktualizacje i odinstalowanie.
|
||||
- [Gotowość i rozwiązywanie problemów](../installation.md#check-readiness-for-your-task): diagnostyka jednego agenta lub silnika analizy.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI i Evidence](../cli.md): polecenia, wybór dostawcy, migawki, import/eksport i kody zakończenia.
|
||||
- [Kontrakty MCP](../mcp-contracts.md) i [prompty dla agentów](../mcp-prompts.md): wyniki narzędzi, sesje i prowadzone analizy.
|
||||
- [Katalog narzędzi](../mcp-contracts.md#generated-catalog): generowany podczas kompilacji wykaz narzędzi, dostawców i poleceń CLI.
|
||||
|
||||
@@ -152,6 +152,7 @@ O REA requer Node.js 22.x (>=22.19), 24.x (>=24.11) ou 26+, além do npm. As fer
|
||||
| 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) |
|
||||
| Capturas de pacotes | Endpoints, DNS, TLS SNI, distribuição de protocolos e requisições HTTP | tshark e capinfos fornecidos pelo chamador; [guia pcap](../pcap-analysis.md) |
|
||||
| Assemblies .NET | Metadados, instruções CIL, dependências nativas declaradas e comparações de builds | Inspeção estática; [guia de código gerenciado](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| APKs Android | Declarações do manifesto, classes, métodos descompilados e referências | JADX sem interface gráfica e um JDK completo no Linux/macOS/Windows x64; [guia de Android](../android-analysis.md) |
|
||||
| Dispositivos Android | Dispositivos, pacotes, processos, logs, despejos, capturas de tela e transferências de APK/arquivos | adb fornecido pelo chamador (emulador ou aparelho); [guia de ADB](../adb-device-analysis.md) |
|
||||
| Projetos JEB | Unidades de projeto do motor, hashes de artefatos e pseudocódigo de tipos/métodos | Um cliente JEB iniciado pelo chamador servindo MCP, selecionado com REA_JEB_MCP_URL; [guia de JEB](../jeb-analysis.md) |
|
||||
@@ -264,6 +265,7 @@ Comece pelos [guias práticos](https://rea.tools/pt-BR/guides/) do site. Para op
|
||||
|
||||
- [Instalação e configuração](../installation.md): registro de agentes, configuração de provedores, atualizações e desinstalação.
|
||||
- [Prontidão e solução de problemas](../installation.md#check-readiness-for-your-task): diagnóstico de um agente ou motor de análise específico.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI e Evidence](../cli.md): comandos, seleção de provedores, snapshots, importação/exportação e status de saída.
|
||||
- [Contratos MCP](../mcp-contracts.md) e [prompts para agentes](../mcp-prompts.md): resultados das ferramentas, sessões e investigações guiadas.
|
||||
- [Catálogo de ferramentas](../mcp-contracts.md#generated-catalog): inventário de ferramentas, provedores e comandos da CLI gerado durante o build.
|
||||
|
||||
@@ -162,6 +162,7 @@ REA требует Node.js 22.x (>=22.19), 24.x (>=24.11) или 26+, а так
|
||||
| Записанные логические сигналы | Аннотации декодера, диапазоны отсчётов и диагностика | 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) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
|
||||
Статическое исследование JavaScript и .NET читает предоставленные файлы, не запуская приложение. Захват во время выполнения запускает выбранную цель или взаимодействует с ней с правами вашего пользователя; соответствующие руководства описывают последствия.
|
||||
|
||||
@@ -264,6 +265,7 @@ npx rea-agents@latest setup
|
||||
|
||||
- [Установка и настройка](../installation.md): регистрация агентов, настройка провайдеров, обновление и удаление.
|
||||
- [Готовность и устранение неполадок](../installation.md#check-readiness-for-your-task): диагностика конкретного агента или движка анализа.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI и Evidence](../cli.md): команды, выбор провайдера, снимки, импорт/экспорт и коды завершения.
|
||||
- [Контракты MCP](../mcp-contracts.md) и [промпты агентов](../mcp-prompts.md): результаты инструментов, сеансы и исследования с подсказками.
|
||||
- [Каталог инструментов](../mcp-contracts.md#generated-catalog): создаваемый при сборке перечень инструментов, провайдеров и команд CLI.
|
||||
|
||||
@@ -152,6 +152,7 @@ REA ต้องใช้ Node.js 22.x (>=22.19), 24.x (>=24.11) หรือ 26
|
||||
| ข้อมูลเครือข่ายที่บันทึกไว้ | คำขอ คำตอบ เพย์โหลดที่ปรากฏ และตำแหน่งต้นทาง | HAR; mitmdump บน Linux สำหรับข้อมูลที่บันทึกในรูปแบบเนทีฟของ mitmproxy; [คู่มือการบันทึกข้อมูล](../web-network-captures.md) |
|
||||
| การจับแพ็กเก็ต | จุดเชื่อมต่อ, DNS, TLS SNI, การกระจายโปรโตคอล และคำขอ HTTP | tshark และ capinfos ที่ผู้เรียกจัดเตรียม; [คู่มือ pcap](../pcap-analysis.md) |
|
||||
| แอสเซมบลี .NET | เมทาดาทา คำสั่ง CIL การพึ่งพาเนทีฟที่ประกาศไว้ และการเปรียบเทียบผลการบิลด์ | การตรวจสอบแบบสถิต; [คู่มือโค้ดแบบจัดการ](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| APK ของ Android | การประกาศใน manifest คลาส เมธอดที่ดีคอมไพล์ และการอ้างอิง | JADX แบบไม่มี GUI และ 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) |
|
||||
@@ -264,6 +265,7 @@ npx rea-agents@latest setup
|
||||
|
||||
- [การติดตั้งและตั้งค่า](../installation.md): การลงทะเบียนเอเจนต์ การตั้งค่าผู้ให้บริการ การอัปเดต และการถอนการติดตั้ง
|
||||
- [ความพร้อมใช้งานและการแก้ไขปัญหา](../installation.md#check-readiness-for-your-task): ตรวจวินิจฉัยเอเจนต์หรือเอนจินวิเคราะห์หนึ่งตัว
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI และ Evidence](../cli.md): คำสั่ง การเลือกผู้ให้บริการ สแนปช็อต การนำเข้า/ส่งออก และรหัสการสิ้นสุด
|
||||
- [สัญญา MCP](../mcp-contracts.md) และ[พรอมป์ต์สำหรับเอเจนต์](../mcp-prompts.md): ผลลัพธ์เครื่องมือ เซสชัน และการตรวจสอบตามคำแนะนำ
|
||||
- [แค็ตตาล็อกเครื่องมือ](../mcp-contracts.md#generated-catalog): รายการเครื่องมือ ผู้ให้บริการ และคำสั่ง CLI ที่สร้างระหว่างการบิลด์
|
||||
|
||||
@@ -156,6 +156,7 @@ REA; Node.js 22.x (>=22.19), 24.x (>=24.11) veya 26+ ve npm gerektirir. Ek araç
|
||||
| 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) |
|
||||
| .NET derlemeleri | Meta veriler, CIL talimatları, bildirilmiş yerel bağımlılıklar ve derleme karşılaştırmaları | Statik inceleme; [yönetilen kod rehberi](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.md) |
|
||||
| Android APK'leri | Manifest bildirimleri, sınıflar, tersine derlenmiş metotlar ve referanslar | Linux/macOS/Windows x64 üzerinde arayüzsüz JADX ve tam bir JDK; [Android rehberi](../android-analysis.md) |
|
||||
| Android cihazları | Cihazlar, paketler, süreçler, günlükler, dökümler, ekran görüntüleri ve APK/dosya aktarımları | Çağıranın sağladığı adb (emülatör veya cihaz); [ADB rehberi](../adb-device-analysis.md) |
|
||||
| JEB projeleri | Motor destekli proje birimleri, artefakt özetleri ve tür/metot sözdekodu | Çağıranın başlattığı, MCP sunan bir JEB istemcisi, REA_JEB_MCP_URL ile seçilir; [JEB rehberi](../jeb-analysis.md) |
|
||||
@@ -269,6 +270,7 @@ Web sitesindeki [uygulamalı rehberlerden](https://rea.tools/tr/guides/) başlay
|
||||
|
||||
- [Yükleme ve kurulum](../installation.md): ajan kaydı, sağlayıcı yapılandırması, güncellemeler ve kaldırma.
|
||||
- [Hazırlık ve sorun giderme](../installation.md#check-readiness-for-your-task): belirli bir ajanı veya analiz motorunu tanılama.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI ve Evidence](../cli.md): komutlar, sağlayıcı seçimi, anlık görüntüler, içe/dışa aktarma ve çıkış durumları.
|
||||
- [MCP sözleşmeleri](../mcp-contracts.md) ve [ajan istemleri](../mcp-prompts.md): araç sonuçları, oturumlar ve yönlendirmeli araştırmalar.
|
||||
- [Araç kataloğu](../mcp-contracts.md#generated-catalog): derleme sırasında oluşturulan araç, sağlayıcı ve CLI komutu envanteri.
|
||||
|
||||
@@ -152,6 +152,7 @@ REA потребує Node.js 22.x (>=22.19), 24.x (>=24.11) або 26+, а та
|
||||
| Збережені мережеві захоплення | Запити, відповіді, доступні корисні дані та місця в джерелі | HAR; mitmdump у Linux для захоплень у власному форматі mitmproxy; [посібник мережевих захоплень](../web-network-captures.md) |
|
||||
| Захоплення пакетів | Кінцеві точки, DNS, TLS SNI, розподіл протоколів та HTTP-запити | Надані викликаючою стороною tshark та capinfos; [підручник pcap](../pcap-analysis.md) |
|
||||
| Збірки .NET | Метадані, інструкції CIL, оголошені нативні залежності й порівняння збірок | Статична перевірка; [посібник керованого коду](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.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) |
|
||||
@@ -264,6 +265,7 @@ npx rea-agents@latest setup
|
||||
|
||||
- [Встановлення й налаштування](../installation.md): реєстрація агентів, конфігурація провайдерів, оновлення та видалення.
|
||||
- [Готовність та усунення неполадок](../installation.md#check-readiness-for-your-task): діагностика конкретного агента або рушія аналізу.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI та Evidence](../cli.md): команди, вибір провайдера, знімки, імпорт/експорт і статуси завершення.
|
||||
- [Контракти MCP](../mcp-contracts.md) і [промпти агентів](../mcp-prompts.md): результати інструментів, сеанси й керовані дослідження.
|
||||
- [Каталог інструментів](../mcp-contracts.md#generated-catalog): створюваний під час збірки перелік інструментів, провайдерів і команд CLI.
|
||||
|
||||
@@ -162,6 +162,7 @@ REA yêu cầu Node.js 22.x (>=22.19), 24.x (>=24.11) hoặc 26+, cùng với np
|
||||
| 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) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.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ó.
|
||||
|
||||
@@ -264,6 +265,7 @@ Bắt đầu với [hướng dẫn thực hành](https://rea.tools/vi/guides/) t
|
||||
|
||||
- [Cài đặt và thiết lập](../installation.md): đăng ký tác nhân, cấu hình trình phân tích, cập nhật và gỡ cài đặt.
|
||||
- [Kiểm tra mức sẵn sàng và khắc phục sự cố](../installation.md#check-readiness-for-your-task): chẩn đoán một tác nhân hoặc bộ máy phân tích cụ thể.
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI và Evidence](../cli.md): lệnh, lựa chọn trình phân tích, ảnh chụp trạng thái, nhập/xuất và trạng thái thoát.
|
||||
- [Hợp đồng MCP](../mcp-contracts.md) và [prompt cho tác nhân](../mcp-prompts.md): kết quả công cụ, phiên làm việc và điều tra có hướng dẫn.
|
||||
- [Danh mục công cụ](../mcp-contracts.md#generated-catalog): danh sách công cụ, trình phân tích và lệnh CLI được tạo khi dựng dự án.
|
||||
|
||||
@@ -156,6 +156,7 @@ REA 需要 Node.js 22.x(>=22.19)、24.x(>=24.11)或 26+,以及 npm。
|
||||
| 已保存的網路擷取 | 請求、回應、可訪問的酬載和來源位置 | HAR;原生 mitmproxy 擷取需要 Linux 上的 mitmdump;[擷取指南](../web-network-captures.md) |
|
||||
| 封包擷取 | 端點、DNS、TLS SNI、通訊協定分佈和 HTTP 請求 | 呼叫端提供的 tshark 和 capinfos;[pcap 指南](../pcap-analysis.md) |
|
||||
| .NET 程式集 | 中繼資料、CIL 指令、聲明的原生依賴和建置對比 | 靜態檢查;[托管程式碼指南](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.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) |
|
||||
@@ -270,6 +271,7 @@ npx rea-agents@latest setup
|
||||
|
||||
- [安裝與設定](../installation.md):AI 代理註冊、提供者組態、更新和卸載。
|
||||
- [就緒檢查與故障排查](../installation.md#check-readiness-for-your-task):診斷特定 AI 代理或分析引擎。
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI 與 Evidence](../cli.md):命令、提供者選擇、快照、匯入/匯出和退出狀態。
|
||||
- [MCP 契約](../mcp-contracts.md)和[AI 代理提示詞](../mcp-prompts.md):工具結果、會話和引導式調查。
|
||||
- [工具目錄](../mcp-contracts.md#generated-catalog):建置時生成的工具、提供者和 CLI 命令清單。
|
||||
|
||||
@@ -156,6 +156,7 @@ REA 需要 Node.js 22.x(>=22.19)、24.x(>=24.11)或 26+,以及 npm。
|
||||
| 已保存的网络捕获 | 请求、响应、可访问的载荷和来源位置 | HAR;原生 mitmproxy 捕获需要 Linux 上的 mitmdump;[捕获指南](../web-network-captures.md) |
|
||||
| 数据包捕获 | 端点、DNS、TLS SNI、协议分布和 HTTP 请求 | 调用方提供的 tshark 和 capinfos;[pcap 指南](../pcap-analysis.md) |
|
||||
| .NET 程序集 | 元数据、CIL 指令、声明的原生依赖和构建对比 | 静态检查;[托管代码指南](../managed-code-analysis.md) |
|
||||
| PyInstaller | CArchive, PYZ, Python bytecode | Static inspection; [Python packaged applications](../python-packaged-applications.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) |
|
||||
@@ -270,6 +271,7 @@ npx rea-agents@latest setup
|
||||
|
||||
- [安装与设置](../installation.md):智能体注册、提供方配置、更新和卸载。
|
||||
- [就绪检查与故障排查](../installation.md#check-readiness-for-your-task):诊断特定智能体或分析引擎。
|
||||
- [GGUF / safetensors](../model-artifacts.md)
|
||||
- [CLI 与 Evidence](../cli.md):命令、提供方选择、快照、导入/导出和退出状态。
|
||||
- [MCP 契约](../mcp-contracts.md)和[智能体提示词](../mcp-prompts.md):工具结果、会话和引导式调查。
|
||||
- [工具目录](../mcp-contracts.md#generated-catalog):构建时生成的工具、提供方和 CLI 命令清单。
|
||||
|
||||
@@ -21,6 +21,67 @@ requires an absolute filesystem path and preserves that exact selection:
|
||||
|
||||
## Observations
|
||||
|
||||
Complete delivery remains the default. For a dependency question, select the
|
||||
needed records on the first call instead of returning the complete graph:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_rust_binary",
|
||||
"arguments": {
|
||||
"path": "/artifacts/application",
|
||||
"view": {
|
||||
"collection": "packages",
|
||||
"names": ["serde", "serde_derive"],
|
||||
"fields": ["name", "version", "source", "dependencies"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`detail: "summary"` returns section ranges, root index, package/edge/cycle counts
|
||||
and compiler-hash count. A `view` selects compact delivery automatically;
|
||||
combining it with explicit `detail: "complete"` is an input error. The complete
|
||||
parent Evidence is retained in the MCP session and remains exportable. Subsequent
|
||||
`inspect_analysis_view` calls accept that `parent_evidence_id`, or its portable
|
||||
Evidence file, without reopening the binary:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "ev_<parent digest>"
|
||||
},
|
||||
"view": {
|
||||
"kind": "rust",
|
||||
"collection": "edges",
|
||||
"from_indices": [12],
|
||||
"limit": 20
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Package filters accept exact `names`, `versions` and original `indices`; edge
|
||||
filters accept original edge `indices`, `from_indices` and `to_indices`.
|
||||
Filters intersect, and an empty filter array selects nothing. `offset` counts
|
||||
matching records; `limit` defaults to 64. Pages retain original indices scoped
|
||||
to the parent Evidence, selected fields, examined coverage and the next offset.
|
||||
Default package fields omit dependency vectors; explicitly select
|
||||
`dependencies` to obtain direct references. No dependency path is traversed.
|
||||
|
||||
The CLI uses the same views and requires an exclusive output bundle for compact
|
||||
delivery so the complete Evidence survives process exit:
|
||||
|
||||
```bash
|
||||
rea inspect-rust-binary /artifacts/application --json \
|
||||
--view '{"collection":"packages","names":["serde"],"fields":["name","version"]}' \
|
||||
--evidence_output /artifacts/rust-evidence.json
|
||||
```
|
||||
|
||||
Compact results distinguish complete embedded-record coverage from unknown
|
||||
runtime dependency coverage. A missing section remains unknown rather than an
|
||||
empty dependency set. Retained views describe the captured artifact digest;
|
||||
they do not assert that the current file or running application is unchanged.
|
||||
|
||||
The inline Evidence result contains the artifact path, SHA-256, byte length,
|
||||
container format, architecture, word size, and byte order. When a `.dep-v0`
|
||||
section is present, `auditable_metadata` contains:
|
||||
|
||||
@@ -102,8 +102,29 @@ bounds simultaneous input buffering and private snapshot/worker counts.
|
||||
|
||||
The database and WAL together are limited to 256 MiB. SQLite values/records,
|
||||
SQL statements and the complete worker JSON reply each have a 16 MiB limit.
|
||||
The owned worker has a 256 MiB JavaScript heap limit and a 30-second inspection
|
||||
deadline. SQLite native allocations are separate from the JavaScript heap;
|
||||
The owned worker has a 256 MiB JavaScript heap limit and defaults to a 30,000 ms
|
||||
deadline. Select optional MCP `worker_timeout_ms`, or CLI
|
||||
`--worker-timeout-ms <milliseconds>`, to change the budget for that call. Values
|
||||
must be integer milliseconds from 1 to 2,147,483,647 (the runtime timer range).
|
||||
It covers worker startup, queries and reply writing, **not** DB/WAL snapshot
|
||||
acquisition, final reply reads, cleanup or delivery. Allow corresponding headroom
|
||||
in the MCP client's whole-call deadline; increasing only that deadline does not
|
||||
change the worker budget. No server restart is needed.
|
||||
|
||||
The result's `worker_timeout_ms` reports the actual parent-supervised budget,
|
||||
including the default. Historical Evidence without this field has unknown
|
||||
execution settings, not an inferred default. Explicit input selection is also
|
||||
retained in Evidence parameters; omission stays omitted. Completed observations
|
||||
retained under cancellation or cleanup failure keep the same budget facts.
|
||||
Timeout errors name the selected milliseconds, exact CLI/MCP controls, and
|
||||
cleanup-before-retry requirements. Incomplete cleanup remains a blocker even
|
||||
when a larger budget is available.
|
||||
|
||||
For example, `rea inspect-sqlite-database ./application.sqlite
|
||||
--worker-timeout-ms 60000 --json` selects 60 seconds; equivalent MCP input is
|
||||
`{"path":"/artifacts/application.sqlite","worker_timeout_ms":60000}`.
|
||||
|
||||
SQLite native allocations are separate from the JavaScript heap;
|
||||
overall native heap and total resident memory are not capped. Row limits do not
|
||||
override these byte and resource limits. Resource-limit failures do not return
|
||||
an incomplete schema as successful complete evidence.
|
||||
|
||||
+200
-50
@@ -69,11 +69,18 @@ Source projects also contain large capacity regressions; `test:fast` promises
|
||||
no compiled-runtime prerequisite, not a fixed time budget.
|
||||
|
||||
`npm run lint` prints errors and a warning summary; `npm run jscpd` prints
|
||||
duplication statistics. Complete reports remain in `.cache/static-checks/` and
|
||||
CI uploads them as `static-check-reports`, including after a failed static gate.
|
||||
duplication statistics. Complete reports remain in `.cache/static-checks/`.
|
||||
The fast gate uploads them as `static-check-reports`. Full qualification
|
||||
uploads duplication diagnostics from the deep job as
|
||||
`static-deep-check-reports`, including after that gate fails.
|
||||
Task-graph recording runs `check:ci:fast:tasks` and `check:ci:deep:tasks`
|
||||
without the artifacts lock, so it can record hashes while `check:pr` or
|
||||
`check:test` already holds that lock. The CI jobs still run the locked
|
||||
`check:ci:fast` and `check:ci:deep` commands.
|
||||
Use `npm run lint:details` or `npm run jscpd:details` for all console diagnostics.
|
||||
Both duplicate commands scan production and test code with the same 3% limit;
|
||||
the summary commands preserve checker failures and restore reports from cached
|
||||
Both duplicate commands scan production and test code using the limit in
|
||||
[`.jscpd.json`](https://github.com/morluto/rea/blob/main/.jscpd.json).
|
||||
The summary commands preserve checker failures and restore reports from cached
|
||||
static tasks. Both lint caches include the website, documentation configuration,
|
||||
and experiment code scanned by the checker; a regression compares the actual
|
||||
lint inventory with Turbo's resolved inputs.
|
||||
@@ -140,6 +147,37 @@ the fixture cache. These subprocesses are outside aggregate Vitest coverage;
|
||||
subprocess coverage attachment remains disabled. Cold-start import checks launch
|
||||
independently without this fixture cache.
|
||||
|
||||
`analyzeJavaScriptDirectory` stdio MCP cases in one `cliTest` file share one
|
||||
server and that compile cache. The first case pays startup; later cases reuse
|
||||
the process. File teardown waits for startup still in flight, closes a server
|
||||
registered during that wait, and does not open a server after close. A failed
|
||||
handshake stays the caller error when its cleanup also fails, including a
|
||||
rejection without a reason. That cleanup stays owned until a later close or
|
||||
call releases it. Another server does not open while the cleanup remains, and
|
||||
a cleanup that already succeeded is not run again. Pass
|
||||
`mcpCall` when a case needs its own server, environment, or lifecycle. CLI
|
||||
cases for that helper share one compiled CLI process per child environment.
|
||||
The first case pays process startup; later cases reuse the loaded runtime.
|
||||
The process restarts when the environment changes, after 32 commands, or when
|
||||
the worker reports a resident set of at least 1 GiB. A fatal worker failure
|
||||
or a broken session frame retires that process and waits for it to exit before
|
||||
the next command starts.
|
||||
`cli.run` still starts a fresh process, including stdin and explicit deadlines.
|
||||
`runFileCli` uses the file session for commands that have neither. Go binary,
|
||||
IL2CPP metadata, and dylib-resolution commands use that session. File teardown
|
||||
stops the shared CLI process, including startup still in flight, and does not
|
||||
open another after close.
|
||||
`exportedFunctionBindingsCli.test.ts` keeps its own compiled CLI parity
|
||||
process for the file and still compares that process's JSON command results
|
||||
with the MCP session. A command that does not respond is rejected. That
|
||||
process stays owned until it exits, and another process does not start while
|
||||
it remains. A process that stays running after SIGKILL remains owned so a
|
||||
later close can retry. Closing the session reports a failure when the process
|
||||
does not exit after stdin closes, once the child has been stopped.
|
||||
`applicationWorkflowCli.test.ts` keeps one compiled CLI process for its JSON
|
||||
commands, including a non-zero status whose stdout is still a JSON document,
|
||||
and closes that process after the file.
|
||||
|
||||
The Apple artifact verifier step shares a job-temporary bytecode cache among
|
||||
its fresh CLI/MCP children via `REA_ARTIFACT_NODE_COMPILE_CACHE`. The artifact
|
||||
helper maps this setting to child `NODE_COMPILE_CACHE`; npm, Turbo, and nested
|
||||
@@ -254,7 +292,23 @@ termination. Simulated Windows controls do not establish native Windows behavior
|
||||
Native process-ownership controls distinguish start identity, zombie lifecycle,
|
||||
current parent/group relation, and token-read evidence. Retain generation-bound
|
||||
candidates through smaller lineage observations, table omissions, reparenting,
|
||||
group changes, and failed cleanup retries. A readable token with unavailable
|
||||
group changes, and failed cleanup retries. Identityless exit-refresh controls
|
||||
cover a replacement row that is not linked to the prior descendant set: a
|
||||
matching token on its own group is signaled, a foreign or absent token is not,
|
||||
an unreadable live token stays unresolved, a replacement that appears only in
|
||||
the confirmation table is still token-checked, a group discovered during
|
||||
revalidation is signaled before cleanup succeeds, a member that changes groups
|
||||
during confirmation is signaled in its new group, a PID first seen with a
|
||||
foreign token is read again on the confirmation table, a PID whose token was
|
||||
already read is read again even when its parent and group return to the
|
||||
pre-exit values, a pre-existing PID is read again when its parent or group
|
||||
changes, an unchanged pre-existing row that was never token-read stays unread,
|
||||
an unbounded confirmation
|
||||
chain stays unresolved, lineage reads a row again when its launcher-tree
|
||||
membership changes, a cached lineage token is read again on the next refresh, an initial descendant whose token already matched is read again on a later refresh, the launcher is read again on that refresh and a missing, unowned, or relation-changed launcher PID stays unavailable, a descendant absent from the latest refresh is omitted,
|
||||
a group that reappears after it was processed is revalidated once, a further
|
||||
reappearance stays unresolved, lineage publishes one row per PID, and lineage does not publish a
|
||||
verified empty descendant list while that row remains unexamined. A readable token with unavailable
|
||||
identity stays unresolved. Native UID facts, rather than an earlier process-table
|
||||
row, determine whether a token denial concerns another user. Darwin tests compile
|
||||
the helper and exercise snapshot growth, absence, lifecycle, and UID observations;
|
||||
@@ -454,8 +508,8 @@ partial diagnostics and preflights do not establish complete mutation outcomes
|
||||
or qualify the release revision for mutation coverage.
|
||||
|
||||
Fast CI starts the source-only projects after classification, independently of
|
||||
build and static checks, and runs five built-dependent shards after the build.
|
||||
The six test jobs still select every portable test. Full qualification keeps six
|
||||
build and static checks, and runs eight built-dependent shards after the build.
|
||||
Those nine test jobs still select every portable test. Full qualification keeps six
|
||||
complete Vitest shards with coverage. Neither tier retries tests. Canonical
|
||||
project ownership lives in `scripts/lib/test-projects.mjs`, shared by Vitest,
|
||||
CI group execution, and development-test preparation. A test importing compiled
|
||||
@@ -467,9 +521,15 @@ reports even when a shard fails, uploads available diagnostics, and writes the
|
||||
slowest files to the workflow summary. Partial reports are marked diagnostic
|
||||
only. Failed, cancelled, or missing selected shards still fail the required gate,
|
||||
and aggregate coverage thresholds remain enforced in full qualification.
|
||||
Only successful complete full-tier merges publish the small `test-timings` artifact, with
|
||||
checkout-relative paths; the build job downloads that artifact for the sequencer
|
||||
instead of the coverage/JUnit bundle. Timing weights come from Vitest module
|
||||
Only a successful complete merge on the default branch publishes shard history,
|
||||
and only from a push, schedule, or manual run. Fast runs publish
|
||||
`test-timings-fast`. Full runs publish `test-timings-full`. Pull requests do not
|
||||
publish it: the lookup reads one unpaginated artifact page, and pull-request
|
||||
artifacts would crowd out the default branch. The build job downloads the
|
||||
artifact that matches its tier, so fast shards use durations measured without
|
||||
coverage and full shards keep coverage-weighted durations. A tier with no
|
||||
qualifying artifact leaves history unset. The download is that small
|
||||
checkout-relative timing file, not the coverage/JUnit bundle. Timing weights come from Vitest module
|
||||
diagnostics and include tests and hooks, worker preparation, environment,
|
||||
imports, and setup. Import timings already include transform waits, which are
|
||||
not added again. The weights describe file costs under the measured worker and
|
||||
@@ -481,14 +541,16 @@ real-system lanes remain separate jobs so one kind of evidence cannot stand in
|
||||
for another. Workflow syntax and expression validation runs inside the required
|
||||
static-check job when workflow changes select it, avoiding a separate runner.
|
||||
Native, provider, fixture, SQLite compatibility, and website lanes wait for
|
||||
static checks to succeed before allocating their runners. This avoids spending
|
||||
those runner slots on revisions that already fail static validation. Portable
|
||||
test shards and documentation checks remain independent of static results, so
|
||||
they can still provide diagnostic feedback. The required gate rejects skipped
|
||||
selected lanes when static checks fail; passing revisions retain all selected
|
||||
coverage. This ordering reduces peak demand on invalid revisions but can add
|
||||
static-check waiting to otherwise passing runs; it is not a measured reduction
|
||||
in end-to-end latency.
|
||||
the fast static checks (typecheck, built lint, and format) to succeed before
|
||||
allocating their runners. Knip, duplication, architecture guards, test
|
||||
discovery, and the Python producer run in the separate full-tier deep static
|
||||
job and do not gate provider admission. Portable test shards and documentation
|
||||
checks remain independent of static results, so they can still provide
|
||||
diagnostic feedback. The required gate rejects skipped selected lanes when
|
||||
static checks fail; on the full tier it also requires the deep static job.
|
||||
Passing revisions retain all selected coverage. This ordering reduces peak
|
||||
demand on invalid revisions but can add static-check waiting to otherwise
|
||||
passing runs; it is not a measured reduction in end-to-end latency.
|
||||
|
||||
Core jobs have execution limits of five minutes for classification, ten minutes
|
||||
for static checks, builds, docs, and report merging, and thirty minutes per
|
||||
@@ -514,11 +576,35 @@ JSON artifact members without executing artifact content. Reports and limits
|
||||
are retained in `ci-health-diagnostics`; its ten-minute job limit covers the
|
||||
existing four-minute timing collection plus the bounded diagnostic stages.
|
||||
|
||||
Shard-count reduction remains deferred: the available successful timing sample
|
||||
covers an older inventory, and file phase weights are not job wall time. The
|
||||
one-source/five-built split retains six test jobs; it makes no hosted speedup
|
||||
claim. Current complete timings and completed candidate hosted runs are needed
|
||||
before enabling a smaller adaptive matrix.
|
||||
Fast built shards target under ten minutes of uncontended shard-job execution.
|
||||
That figure is the `test-shard` job. The job waits for classification and the
|
||||
build, and the portable-test merge runs after the slowest shard. On 2026-10-20,
|
||||
run `39451548945` measured those surrounding jobs with runner queue excluded:
|
||||
classification 15 seconds, build 49 seconds, and the portable-test merge 29
|
||||
seconds. Shard 1/5 of that run spent 22 seconds before built Vitest and 3
|
||||
seconds after it, including `npm ci` at 9 seconds. `node_modules` is about 674
|
||||
MiB, so publishing it as a shared artifact would transfer more data than that
|
||||
install saves. The npm download cache stays the setup cache.
|
||||
|
||||
File timings from that run put the built suite at 7944 seconds of module time.
|
||||
The sequencer keeps each file's full duration on one worker. Extra workers
|
||||
overlap other files in the same sequence group, and `process-boundary` runs
|
||||
after that group. Scheduling the unsplit inventory onto five shards peaks at
|
||||
820 seconds of tests. That run's slowest built shard job lasted 925 seconds
|
||||
(shard 4/5), and shard 1/5 spent 874 seconds in built Vitest, so hosted job
|
||||
time sits above the file schedule. Six shards of the same inventory peak at
|
||||
684 seconds of tests. Eight shards, after the 578-second loader file is split
|
||||
by case count (54 cases stay in that file and 46 move into each of three
|
||||
files), peak at 513 seconds of tests. With the measured 25-second shard setup,
|
||||
that shard job is about 538 seconds. Adding the measured classification, build,
|
||||
and merge makes an uncontended portable critical path of about 631 seconds,
|
||||
still before the gap between this file schedule and hosted Vitest. Runner queue
|
||||
is separate and grows when more shard jobs compete for runners.
|
||||
`process-boundary` stays serial because process-tree sampling shares the host.
|
||||
Raising the worker cap from two to four on this four-core host cut a six-file
|
||||
acceptance sample from 96–101 seconds to 71 seconds and did not remove the
|
||||
single-file floor, so the cap stays two. Full qualification keeps six covered
|
||||
shards; its longer instrumentation is outside this fast budget.
|
||||
|
||||
The separate `CI timings` workflow batches recent completed CI and Release runs
|
||||
hourly or on manual dispatch. It uploads `ci-job-timings-batch` with raw API pages and
|
||||
@@ -560,8 +646,9 @@ metadata, and available manifests into `.cache/ci-build-cache/`.
|
||||
`portable-build-cache.json` records availability per task. Evicted or partially
|
||||
missing entries are omitted as whole entries, and consumers rebuild cache misses,
|
||||
including when every entry is unavailable. Malformed plans, non-file entries,
|
||||
and I/O errors other than disappearance still fail publication. Older cross-run entries stay in the
|
||||
original cache. The Windows lane relies on the Linux static job for typechecking. They retain the normal build and prepack
|
||||
and I/O errors other than disappearance still fail publication. After a trusted
|
||||
static run, superseded cross-run Turbo snapshots for that OS and architecture
|
||||
are deleted down to the two newest generations. The Windows lane relies on the Linux static job for typechecking. They retain the normal build and prepack
|
||||
commands, which restore matching outputs or rebuild on a cache miss. Native
|
||||
artifacts, dependency installs, package archives, and runtime verification remain
|
||||
host-local. Keep platform-dependent outputs out of this shared cache.
|
||||
@@ -1110,7 +1197,7 @@ suite, without the source-test shards or native package
|
||||
lanes. Classification compares the PR head with its merge base, so later
|
||||
base-branch changes do not expand that scope. Static and portable-test aggregate jobs
|
||||
remain present and fail if classification fails. Every code change retains the
|
||||
complete six-shard Linux suite; individual test files are not selected by
|
||||
complete portable Linux suite; individual test files are not selected by
|
||||
imports or filenames.
|
||||
|
||||
Ordinary PRs also produce an **observational** test-selection plan. The candidate
|
||||
@@ -1121,7 +1208,7 @@ tests. Shared inputs, unknown paths, missing owner tests, and removed tests fall
|
||||
back to the complete inventory. Renames account for both paths. Main, scheduled,
|
||||
manual, and `ci:full` runs always propose the full suite.
|
||||
|
||||
All six shards still execute the complete suite. The existing report merge
|
||||
The source group and every built shard together still execute the complete suite. The existing report merge
|
||||
compares the proposal with actual module outcomes and retains
|
||||
`.cache/ci-test-selection/report.json` in `test-results`, including selected and
|
||||
omitted paths, changed input paths, missed failures, unobserved files, scheduling weights, PR head and
|
||||
@@ -1167,13 +1254,16 @@ scripts also select it. Unrelated code edits retain the complete portable suite
|
||||
on the pinned Node version without starting four additional compatibility jobs.
|
||||
Full qualification retains every SQLite version.
|
||||
Shared process/contract/CLI boundaries, dependencies, other packaging inputs,
|
||||
unknown paths, and changes to the planner select the full baseline.
|
||||
unknown paths, and changes to the planner select the fast Linux x64 baseline:
|
||||
portable code checks and the installed-package runtime lane, without provider
|
||||
lanes or the multi-host package matrix. A shared file with exactly one owner
|
||||
still selects that owner. Full qualification still selects every lane.
|
||||
CI timing collection, timing history, and test-report helper edits retain static
|
||||
checks and the complete portable suite without selecting unrelated host/provider
|
||||
lanes. Classification, qualification, gate, release receipt, and unknown CI
|
||||
scripts keep the broad fallback.
|
||||
scripts keep that fast baseline.
|
||||
Package script-only edits select their owning verifier when its entrypoint is
|
||||
identifiable; unknown or shared script changes select everything. Android
|
||||
identifiable; unknown or shared script changes select the fast baseline. Android
|
||||
build-script edits retain the Linux suite and workflow validation; real Android
|
||||
verification stays manual. Renames account for both paths.
|
||||
|
||||
@@ -1197,9 +1287,10 @@ or provider effects outside its owners.
|
||||
Ordinary main pushes use the same owner scopes as PRs. Their diff starts at a
|
||||
successful ancestor push run of this CI workflow, rather than the preceding
|
||||
push: changes from failed or cancelled intermediate runs remain in scope.
|
||||
Missing, untrusted, or non-ancestor history selects every lane. The lookup is
|
||||
Missing, untrusted, or non-ancestor history selects the fast Linux x64
|
||||
baseline: portable checks and the Linux runtime lane. The lookup is
|
||||
bounded to the last 100 successful push runs; lookup failures broaden selection.
|
||||
Every code change still selects all six portable shards, without coverage
|
||||
Every code change still selects the source group and all eight fast built shards, without coverage
|
||||
instrumentation in this fast tier. Ordinary PR matrices cancel remaining
|
||||
shards after the first failure; passing PRs execute the complete suite. Main,
|
||||
`ci:full`, scheduled, manual, and release qualification retain every shard's
|
||||
@@ -1227,8 +1318,11 @@ immediately starts the separate `Full PR qualification` workflow against the
|
||||
label event's merge revision; subsequent PR revisions also select the full tier
|
||||
in normal CI while that label remains. Unrelated labels do not start validation
|
||||
jobs or replace the main CI checks. Manual dispatch also requests full validation.
|
||||
Fast-tier broad fallbacks retain all owner lanes and the three-host
|
||||
package matrix; Intel macOS and coverage remain full-tier obligations. Each
|
||||
Fast-tier broad fallbacks keep the portable suite and the Linux x64 runtime
|
||||
baseline. They do not start every owner lane or the three-host package
|
||||
matrix. Intel macOS and coverage remain full-tier obligations. Real provider
|
||||
lanes stay path-gated, and a shared registration file listed by many lanes
|
||||
does not start those lanes on a fast PR. Each
|
||||
classification job records its selection and broad-fallback reasons in the run
|
||||
summary. Representative Git-diff cases live in
|
||||
`tests/boundary/filesystem/ciChangeScope.test.ts`.
|
||||
@@ -1302,15 +1396,33 @@ catalog, and managed-conformance metadata. Each shard still installs the locked
|
||||
dependencies and runs its assigned tests; full qualification enables coverage. It no longer
|
||||
regenerates the same metadata independently.
|
||||
|
||||
The build job also finds an available `test-timings` artifact from a successful
|
||||
trusted default-branch scheduled or manual qualification and includes that same
|
||||
report in each shard's snapshot. The bounded lookup verifies the artifact's
|
||||
producer; newer schedules that reuse qualification without producing timings do
|
||||
not hide the last full run's artifact.
|
||||
The build job finds the newest trusted timing artifact for its tier
|
||||
(`test-timings-fast` or `test-timings-full`) from a successful default-branch
|
||||
push, schedule, or manual CI run, and includes that same report in each shard's
|
||||
snapshot. The bounded lookup verifies the producer. Pull-request runs and other
|
||||
branches are not producers. A tier with no qualifying artifact leaves history
|
||||
unset, and Vitest uses its own assignment. A later schedule that reuses
|
||||
qualification without uploading timings leaves the previous artifact in place.
|
||||
|
||||
On 2026-10-21 the newest full history was run
|
||||
`39545047294`, recorded before compiled-CLI session reuse. Fast run
|
||||
`39552964873` packed its eight built shards from that history, and those shard
|
||||
jobs executed for 319–591 seconds. Replaying that run's file durations through
|
||||
the sequencer's two-worker schedule balances every shard at about 404 seconds
|
||||
of file time. The same replay with the stale history weights leaves the slowest
|
||||
shard near 547 seconds and the fastest near 293. That spread is file-schedule
|
||||
time, not runner queue, which is why each tier keeps its own history.
|
||||
CI assigns discovered files by historical duration, with a deterministic
|
||||
fallback weight for new files, rather than dividing only by file count. Every
|
||||
discovered file still belongs to exactly one shard; worker limits and project
|
||||
ordering remain unchanged. The merge job rejects successful reports with missing,
|
||||
fallback weight for new files, rather than dividing only by file count.
|
||||
Each file keeps its full duration on one worker. Extra workers overlap other
|
||||
files in the same sequence group, and `process-boundary` runs after that
|
||||
group. History published before the competing-loader split still names only
|
||||
`javascriptLiteralPercentPaths.test.ts`. Until that artifact records one of
|
||||
the three new files, the sequencer assigns that recorded duration by the
|
||||
pre-split case counts: 54/192 to the remaining file and 46/192 to each new
|
||||
file. Every discovered file still belongs to exactly one shard; worker limits
|
||||
and project ordering remain unchanged. Fast qualification keeps eight built
|
||||
shards without coverage. The merge job rejects successful reports with missing,
|
||||
unexpected, or duplicate portable files. Coverage instrumentation and aggregate
|
||||
thresholds remain enforced in full qualification.
|
||||
Missing or unusable history falls back to Vitest's default assignment. Only
|
||||
@@ -1318,18 +1430,56 @@ timing data is reused; live provider results are never cached. Turbo outputs are
|
||||
restored by task hash, with trusted default-branch runs as cache writers and PRs
|
||||
as restore-only consumers. The static job also restores its Turbo snapshot;
|
||||
each task revalidates its own input hash before reusing outputs or diagnostics.
|
||||
The Node pin and npm install settings are global hash inputs. Knip includes its ignore rules and VitePress
|
||||
The Node pin and npm install settings are global hash inputs. Knip runs only
|
||||
in full qualification, with `--cache` and a GitHub cache of
|
||||
`node_modules/.cache/knip`. The saved key includes the commit SHA. Restore uses
|
||||
the configuration prefix, so the next trusted full run can publish a refreshed
|
||||
snapshot. That cache is an AST cache keyed by file mtime and size, so a
|
||||
fresh checkout often misses it; Turbo still hashes the Knip task inputs. Knip
|
||||
includes its ignore rules and VitePress
|
||||
entry, so changes to those inputs cannot reuse an older successful check.
|
||||
Jobs that run Turbo export `TURBO_TEAM`. A called workflow sees the caller
|
||||
event, so `workflow_call` is not an event name inside CI. `TURBO_TOKEN` is set
|
||||
when release qualification passes `remote_cache_write`, or when the ref is the
|
||||
default branch and the event is push, schedule, or workflow_dispatch, and only
|
||||
when that secret is available to the run. Release qualification passes
|
||||
`TURBO_TOKEN` and `remote_cache_write` into the called workflow. Labeled
|
||||
pull-request qualification does not. Pull requests receive an empty token and
|
||||
`TURBO_CACHE=local:rw`. Remote reads require `TURBO_TOKEN`, and this repository
|
||||
does not pass a separate read-only credential into pull requests. An empty
|
||||
token leaves the local Turbo cache in place.
|
||||
|
||||
The shared npm-cache action caches downloads, not `node_modules`, and every
|
||||
consumer still runs `npm ci`. Keys cover the runner OS, actual Node architecture,
|
||||
root lockfiles, and `.npmrc`. Only push, schedule, and manual events on the
|
||||
repository's default branch may save shared npm caches; PRs restore only.
|
||||
Static snapshots use the same write authority and are saved only after the
|
||||
static job's validation succeeds. Main warms the new cache namespace after
|
||||
merge; a cold cache or changed lockfile falls back to the normal install.
|
||||
This prevents each PR from saving another branch-scoped copy of the same
|
||||
download cache. It does not remove runner assignment waits.
|
||||
root lockfiles, and `.npmrc`. An exact-key miss restores the newest cache for
|
||||
that OS and architecture, so a lockfile change reuses unchanged tarballs while
|
||||
`npm ci` still verifies the lockfile. Only push, schedule, and manual events on
|
||||
the repository's default branch may save shared npm caches, and only from the
|
||||
opted-in writer for that host: Static checks for Linux x64, the package matrix
|
||||
for every other included host, and the Windows curated lane. Every other job
|
||||
restores without saving, so parallel jobs do not publish duplicate copies.
|
||||
Pull requests restore only. Static snapshots are saved only after the static
|
||||
job's validation succeeds. Cache deletion is a
|
||||
separate workflow, because reusable qualification calls this workflow and
|
||||
cannot raise its read-only token. After a push, schedule, or manual CI run on
|
||||
the default branch finishes, including when a later lane fails, times out, or
|
||||
is cancelled, that workflow runs on `ubuntu-latest` and deletes older
|
||||
`rea-static-v1`, `rea-static-deep-v1`, and `rea-portable-build-v2` entries for
|
||||
that runner's OS and architecture, plus older `knip-meta-v1` entries for that
|
||||
OS. It keeps two generations plus the key that run saved. A fast run that did
|
||||
not publish a deep or Knip snapshot still leaves the two newest entries in
|
||||
those families. It
|
||||
deletes an npm download record only when another record has the same key, cache
|
||||
version, and git ref, and it keeps the most recently accessed copy. A different
|
||||
version or ref is a different restore target and stays. One cache id is one
|
||||
record: a repeated id keeps the later access time when its key, version, ref,
|
||||
size, and creation time agree, and an inconsistent repeat deletes nothing. An
|
||||
incomplete or unexpected cache list deletes nothing.
|
||||
Distinct lockfile generations of the download cache stay, so the restore
|
||||
prefix still has a seed.
|
||||
Main warms the new cache namespace after merge; a cold cache falls back to the
|
||||
normal install. This prevents each PR from saving another branch-scoped copy of
|
||||
the same download cache. It does not remove runner assignment waits.
|
||||
|
||||
Portable owner-based test selection remains observational. Its reports compare
|
||||
proposed omissions with the full run; incomplete or skipped observations do not
|
||||
|
||||
@@ -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,"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
|
||||
16,src/domain/errors.ts:AnalysisTimeoutError,Analysis deadline expires,"REA's {operation} operation reached its {timeoutMs}ms server/provider deadline; extending only the client deadline does not change this budget",Returned server deadline is distinct from a host timeout that discards the response,"Inspect partial Evidence and reported cleanup state; diagnose the provider or reduce supported work without assuming adjustable limits. JavaScript specifically names analysis_timeout_ms and exact source_paths; omit an unneeded filter rather than passing an empty array.",implemented,projection and public CLI/MCP timeout regression coverage; client recovery also taught during initialization and in the shipped skill
|
||||
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
|
||||
|
||||
|
+32
-1
@@ -26,6 +26,17 @@ execution validates tool files and version banners. Host process-ownership and
|
||||
private-workspace controls must also be available. The real-tool profile has
|
||||
been exercised on macOS arm64; other hosts require their own verification.
|
||||
|
||||
Configuration failures do not establish anything about the selected WASM bytes.
|
||||
For a rejected version probe, `details.diagnostics` identifies the executable,
|
||||
tool, expected version and configuration key; `details.captured_output` preserves
|
||||
its actual stdout and stderr, including empty banners or warnings. Fix the
|
||||
reported directory, executable access or release rather than changing the module
|
||||
or repeating an unchanged call. All three tools must report 1.0.42 on stdout with
|
||||
empty stderr. For CLI calls, set the environment before the next invocation. For
|
||||
MCP, change the server's launch environment and restart/reconnect that server;
|
||||
exporting a variable in a different shell does not update a running server.
|
||||
REA never installs tools or relaxes version admission to recover.
|
||||
|
||||
```bash
|
||||
REA_WABT_BIN_DIRECTORY=/absolute/wabt/bin rea inspect-wasm-artifact \
|
||||
/artifacts/module.wasm --glue_paths /artifacts/glue.js \
|
||||
@@ -64,7 +75,27 @@ The tool profile preserves upstream release/source identity, resolved executable
|
||||
paths, executable digests, exact banners and arguments. Executable digests are
|
||||
checked again after each command. Caller-supplied tools are trusted programs;
|
||||
version and digest recording are not publisher authentication or an OS sandbox.
|
||||
Each subprocess has a 30-second deadline and 32 MiB combined output budget.
|
||||
Each WABT version/analysis command defaults to a 30,000 ms deadline. Set optional
|
||||
MCP `command_timeout_ms`, or CLI `--command-timeout-ms <milliseconds>`, to select a
|
||||
positive integer up to 2,147,483,647 ms (the runtime timer range) for that call.
|
||||
The selected deadline is retained as `tool_profile.timeout_ms` and, when supplied,
|
||||
in Evidence parameters. It applies separately to each command, not the whole
|
||||
inspection, file admission or cleanup. The MCP client must allow time for all
|
||||
commands, file checks, cleanup and delivery. A provider timeout reports the
|
||||
actual selected deadline and these recovery controls; increasing only the client
|
||||
deadline does not change the provider budget. No server restart is required to
|
||||
change this per-call setting. Resolve incomplete cleanup before another attempt.
|
||||
|
||||
For example, `rea inspect-wasm-artifact /artifacts/module.wasm
|
||||
--command-timeout-ms 60000 --format json` uses a 60-second per-command deadline;
|
||||
the equivalent MCP input is `{"path":"/artifacts/module.wasm",
|
||||
"command_timeout_ms":60000,"detail":"summary"}`. Summary delivery retains the
|
||||
complete Evidence but does not reduce WABT analysis work.
|
||||
|
||||
The 8 MiB input allowance and 32 MiB combined command-output budget remain fixed;
|
||||
increasing `command_timeout_ms`, host memory or the client deadline does not raise
|
||||
them. Do not truncate required module bytes to fit. Select another suitable
|
||||
complete artifact only if it answers the question, otherwise report the limit.
|
||||
Inspection output past the budget fails with `resource_constraint`; an oversized
|
||||
`--version` probe still reports `unreadable_output`. Both streams share the
|
||||
budget, so a stream that starts after the other has filled it retains nothing.
|
||||
|
||||
+3
-1
@@ -32,7 +32,9 @@ A document replacement ends the window; final coverage then remains unavailable.
|
||||
|
||||
Same-document navigation such as `history.pushState()` remains valid when the frame and known loader identity stay stable and the current origin is allowed. Unknown loader identity retains a conservative URL check; newly reported or replaced loaders are rejected. Malformed producer events end an armed window immediately and trigger cleanup, independently of the selected duration.
|
||||
|
||||
Target detachment ends the window even when the browser's root connection stays open. When the closed session cannot acknowledge cleanup, the operation returns a failure with the selected target, failed commands and original producer reason; it does not claim successful cleanup.
|
||||
Target detachment ends the window with `session_detached`, even when the browser's root connection stays open. An `Inspector.targetCrashed` event ends it with `target_terminated`. A lost transport records `transport_disconnected` in browser observation; execution capture returns the typed transport failure. These outcomes retain the selected target and the observed lifecycle fact.
|
||||
|
||||
When an ended session cannot acknowledge cleanup, the operation returns a failure with the failed commands and original typed producer reason. A coverage-stop failure also retains the observed window's end reason in its diagnostics; cleanup remains unconfirmed until the owning session can resolve it.
|
||||
|
||||
Child frames, workers, service workers, WebAssembly, chronological execution order and framework-specific causal interpretation are outside this operation. Script ownership comes from frame/context metadata, never URL equality. Repeated URLs and synthetic names therefore retain separate identities.
|
||||
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# Analysis efficiency measurements
|
||||
|
||||
The [2026-10-21 coverage reconciliation](coverage-review-20261021.md) maps all
|
||||
46 source directories to concrete workflow evidence, including original feature
|
||||
PRs, historical no-benefit results and remaining verification boundaries.
|
||||
|
||||
The [GGUF validation checkpoint](model-utf8-validation.md) removes decoding of
|
||||
omitted strings while retaining full UTF-8 validation. It records real-model
|
||||
phase gains, unchanged task token costs and the higher main-task memory peak.
|
||||
|
||||
The [WAT framing checkpoint](wasm-string-framing.md) measures structural scanning
|
||||
on real SQLite and VS Code components, with unchanged Evidence and token costs,
|
||||
small whole-task timing gains and no memory improvement.
|
||||
|
||||
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,
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
"""Count all actual client JSON inputs and preserve bounded family resource receipts."""
|
||||
from pathlib import Path
|
||||
import argparse, json, hashlib
|
||||
import tiktoken
|
||||
parser=argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--root',type=Path,required=True)
|
||||
parser.add_argument('--output',type=Path,required=True)
|
||||
args=parser.parse_args();lab=args.root;enc=tiktoken.get_encoding('cl100k_base')
|
||||
compact=lambda value:json.dumps(value,ensure_ascii=False,separators=(',',':'),allow_nan=False)
|
||||
count=lambda value:len(enc.encode(compact(value),disallowed_special=()))
|
||||
def resources(root):
|
||||
scope=json.loads(Path(str(root)+'.log').read_text().splitlines()[-1]);o=scope['payload_resource_observation']
|
||||
assert scope['abort'] is None and scope['payload_resource_observation_verified'] and o['payload_exit_code']==0 and o['payload_cleanup_verified']
|
||||
for event in o['resource_events'].values():assert not any(event.values())
|
||||
return {'wall_seconds':scope['elapsed_seconds'],'cpu_seconds':o['cpu_stat']['usage_usec']/1e6,'family_peak_mib':o['memory_peak_bytes']/1048576,'payload_cleanup_verified':True,'final_whole_scope_counters_verified':scope['resource_counters_verified']}
|
||||
wire=[];sdk=[]
|
||||
for root in sorted(lab.glob('final-wire-*')):
|
||||
if not root.is_dir():continue
|
||||
report=json.loads((root/'report.json').read_text());requests=json.loads((root/'model-requests.json').read_text());assert report['status'] in ['passed','expected-incomplete-task'] and not report['cleanup_failures'] and report['appserver_exit_code']==0 and report['owned_scope_drain']['remaining_processes']==[]
|
||||
rows=[]
|
||||
for index,captured in enumerate(requests):
|
||||
body=captured['body'];namespace=[n for n in body.get('tools',[]) if n.get('type')=='namespace' and n.get('name')=='mcp__rea_audit'];compaction=not namespace
|
||||
if compaction:assert 'CONTEXT CHECKPOINT COMPACTION' in compact(body['input'][-1])
|
||||
else:assert len(namespace)==1 and len(namespace[0]['tools'])==210
|
||||
rows.append({'index':index+1,'kind':'compaction' if compaction else 'normal','whole_json_tokens_proxy':count(body),'rea_namespace_tokens_proxy':0 if not namespace else count(namespace[0]),'rea_function_count':0 if not namespace else len(namespace[0]['tools'])})
|
||||
wire.append({'case':root.name,'task_completed':report['task_completed'],'mode':report['mode'],'budget':report['observations']['tool_output_budget'],'requests':len(rows),'compactions':sum(r['kind']=='compaction' for r in rows),'whole_json_tokens_proxy':sum(r['whole_json_tokens_proxy'] for r in rows),'request_details':rows,**resources(root)})
|
||||
for root in sorted(lab.glob('final-sdk-*')):
|
||||
if not root.is_dir():continue
|
||||
report=json.loads((root/'report.json').read_text());assert report['status']=='passed' and not report['cleanup_failures'] and not report['receipt_failures']
|
||||
sdk.append({'case':root.name,'provider':report['provider'],'symbol':report['symbol'],'detail':report['detail'],'warm':report['warm'],'tool_calls':len(report['calls']),'exported_bundle_bytes':(root/'evidence.json').stat().st_size,'disassembly_calls':[r for r in report['calls'] if r['phase'].startswith('disassembly:')],**resources(root)})
|
||||
receipt={'measurement_scope':'Actual public SDK and Codex0.163 app-server with real GDB/BFD. Local deterministic SSE fixture and observed-ID checkpoint; no AI inference, model usage/quality/acceptance or billing claim. Whole compact JSON serialization tokens are proxies and include all schemas/replays/compaction. Incomplete default baseline is excluded from completed-workflow gains. Fresh processes have unflushed OS caches; warm SDK cases repeat the selected disassembly in the same server. Native target never executed.','tokenizer':{'package':tiktoken.__version__,'encoding':enc.name},'runtime_pins':{variant:{k:v for k,v in json.loads((lab/(variant+'-manifest.json')).read_text()).items() if k!='files'} for variant in ['baseline','candidate']},'native':json.loads((lab/'native-prerequisite-manifest.json').read_text()),'wire':wire,'sdk':sdk}
|
||||
args.output.write_text(json.dumps(receipt,indent=2)+'\n');print(json.dumps({'wire':[{k:v for k,v in row.items() if k!='request_details'} for row in wire],'sdk':[{k:v for k,v in row.items() if k!='disassembly_calls'} for row in sdk]},indent=2))
|
||||
@@ -0,0 +1,22 @@
|
||||
from pathlib import Path
|
||||
import struct,hashlib,json,argparse
|
||||
parser=argparse.ArgumentParser(description='Independent ELF64 little-endian native-command byte oracle');parser.add_argument('--input',type=Path,required=True);parser.add_argument('--output',type=Path,required=True);args=parser.parse_args();path=args.input;b=path.read_bytes();assert b[:6]==b'\x7fELF\x02\x01'
|
||||
shoff=struct.unpack_from('<Q',b,40)[0];entsize,n,strings=struct.unpack_from('<HHH',b,58)
|
||||
assert entsize==64 and shoff+n*entsize<=len(b)
|
||||
sections=[struct.unpack_from('<IIQQQQIIQQ',b,shoff+i*entsize) for i in range(n)]
|
||||
rows=[]
|
||||
for si,s in enumerate(sections):
|
||||
if s[1]!=11:continue
|
||||
text=sections[s[6]];names=b[text[4]:text[4]+text[5]]
|
||||
assert s[9]==24 and s[5]%s[9]==0
|
||||
for ordinal,off in enumerate(range(s[4],s[4]+s[5],24)):
|
||||
name,info,other,section,value,size=struct.unpack_from('<IBBHQQ',b,off)
|
||||
end=names.find(b'\0',name);assert end>=name
|
||||
if names[name:end] not in [b'execute_command',b'execute_command_internal'] or info&15!=2:continue
|
||||
target=sections[section];file_offset=target[4]+value-target[3]
|
||||
assert target[4]<=file_offset and file_offset+size<=target[4]+target[5]
|
||||
rows.append({'symbol_table_section':si,'symbol_ordinal':ordinal,'name':names[name:end].decode('ascii'),'address':value,'bytes':size,'file_offset':file_offset,'first_16_bytes_hex':b[file_offset:file_offset+16].hex()})
|
||||
assert rows
|
||||
result={'path':str(path),'artifact_sha256':hashlib.sha256(b).hexdigest(),'artifact_bytes':len(b),'elf_class':64,'byte_order':'little','elf_machine':struct.unpack_from('<H',b,18)[0],'symbols':rows,'scope':'Independent ELF64 little-endian dynamic symbol/section mapping; no target execution or REA parser.'}
|
||||
args.output.write_text(json.dumps(result,indent=2)+'\n')
|
||||
print(json.dumps(result))
|
||||
@@ -0,0 +1,212 @@
|
||||
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 { createRequire } from "node:module";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { finished } from "node:stream/promises";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { parseArgs } from "node:util";
|
||||
import { createRetainedQueryCaller } from "./retained-query-calls.mjs";
|
||||
import { finalizeRetainedQueryReceipt } from "./retained-query-finalization.mjs";
|
||||
|
||||
const { values } = parseArgs({
|
||||
options: {
|
||||
runtime: { type: "string" },
|
||||
input: { type: "string" },
|
||||
oracle: { type: "string" },
|
||||
output: { type: "string" },
|
||||
provider: { type: "string" },
|
||||
symbol: { type: "string" },
|
||||
detail: { type: "string", default: "complete" },
|
||||
warm: { type: "boolean", default: false },
|
||||
},
|
||||
});
|
||||
for (const key of ["runtime", "input", "oracle", "output"])
|
||||
assert.equal(values[key], resolve(values[key]));
|
||||
assert.ok(["gdb", "objdump"].includes(values.provider));
|
||||
assert.ok(["complete", "summary"].includes(values.detail));
|
||||
const oracle = JSON.parse(await readFile(values.oracle, "utf8"));
|
||||
assert.ok(oracle.symbols.some((row) => row.name === values.symbol));
|
||||
const inputBytes = await readFile(values.input);
|
||||
assert.equal(
|
||||
createHash("sha256").update(inputBytes).digest("hex"),
|
||||
oracle.artifact_sha256,
|
||||
);
|
||||
const require = createRequire(join(values.runtime, "package.json"));
|
||||
const { Client } = await import(
|
||||
pathToFileURL(require.resolve("@modelcontextprotocol/client"))
|
||||
);
|
||||
const { StdioClientTransport } = await import(
|
||||
pathToFileURL(require.resolve("@modelcontextprotocol/client/stdio"))
|
||||
);
|
||||
await mkdir(values.output, { mode: 0o700 });
|
||||
const account = await mkdtemp(join(tmpdir(), "rea-command-views-"));
|
||||
const client = new Client({ name: "command-observation-task", version: "1" });
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [join(values.runtime, "scripts/rea.mjs"), "mcp"],
|
||||
env: {
|
||||
HOME: account,
|
||||
PATH: process.env.PATH,
|
||||
NODE_OPTIONS: "--max-old-space-size=2048",
|
||||
UV_THREADPOOL_SIZE: "1",
|
||||
REA_LOG_LEVEL: "silent",
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
const serverLog = createWriteStream(join(values.output, "server.log"));
|
||||
const serverLogFinished = finished(serverLog);
|
||||
serverLogFinished.catch(() => {});
|
||||
transport.stderr?.pipe(serverLog);
|
||||
const transcript = [];
|
||||
const report = {
|
||||
question:
|
||||
"Read architecture and the complete byte-annotated disassembly of one selected native function; export complete Evidence. No inferior execution.",
|
||||
provider: values.provider,
|
||||
symbol: values.symbol,
|
||||
detail: values.detail,
|
||||
warm: values.warm,
|
||||
calls: [],
|
||||
observations: [],
|
||||
};
|
||||
const call = createRetainedQueryCaller(client, (receipt, event) => {
|
||||
report.calls.push(receipt);
|
||||
transcript.push(event);
|
||||
});
|
||||
let sessionId;
|
||||
try {
|
||||
await client.connect(transport);
|
||||
const catalog = await client.listTools();
|
||||
await writeFile(join(values.output, "catalog.json"), 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,
|
||||
timeout: 60000,
|
||||
toolDefinition: definitions.get(input.name),
|
||||
});
|
||||
const observe = async (name, input, phase = name) => {
|
||||
const response = await call(name, input, phase);
|
||||
assert.ok(!response.isError, JSON.stringify(response));
|
||||
report.observations.push({
|
||||
name,
|
||||
phase,
|
||||
evidence: response.structuredContent,
|
||||
});
|
||||
return response.structuredContent;
|
||||
};
|
||||
const completeParents = [];
|
||||
const disassemble = async (phase) => {
|
||||
const selection = values.detail === "summary" ? { detail: "summary" } : {};
|
||||
const result =
|
||||
values.provider === "gdb"
|
||||
? await observe(
|
||||
"gdb_console",
|
||||
{
|
||||
session_id: sessionId,
|
||||
command: `disassemble /r ${values.symbol}`,
|
||||
...selection,
|
||||
},
|
||||
phase,
|
||||
)
|
||||
: await observe(
|
||||
"inspect_with_objdump",
|
||||
{
|
||||
path: values.input,
|
||||
operation: "disassemble",
|
||||
symbol: values.symbol,
|
||||
...selection,
|
||||
},
|
||||
phase,
|
||||
);
|
||||
completeParents.push(result);
|
||||
};
|
||||
if (values.provider === "gdb") {
|
||||
const started = await observe("start_gdb_session", {});
|
||||
sessionId = started.session_id;
|
||||
for (const command of [
|
||||
"set auto-load off",
|
||||
"set debuginfod enabled off",
|
||||
`file ${values.input}`,
|
||||
"show architecture",
|
||||
])
|
||||
await observe("gdb_console", { session_id: sessionId, command });
|
||||
} else
|
||||
await observe("inspect_with_objdump", {
|
||||
path: values.input,
|
||||
operation: "file_headers",
|
||||
});
|
||||
await disassemble("disassembly:fresh");
|
||||
if (values.warm) await disassemble("disassembly:repeat");
|
||||
if (sessionId) {
|
||||
await observe("close_gdb_session", { session_id: sessionId });
|
||||
sessionId = undefined;
|
||||
}
|
||||
await observe("export_evidence_bundle", {
|
||||
path: join(values.output, "evidence.json"),
|
||||
});
|
||||
const bundle = JSON.parse(
|
||||
await readFile(join(values.output, "evidence.json"), "utf8"),
|
||||
);
|
||||
for (const delivered of completeParents) {
|
||||
const parent =
|
||||
values.detail === "complete"
|
||||
? delivered
|
||||
: bundle.records.find(
|
||||
(record) =>
|
||||
record.evidence_id ===
|
||||
delivered.normalized_result.parent_evidence_id,
|
||||
);
|
||||
assert.ok(parent);
|
||||
assert.equal(parent.normalized_result.completion_status, "complete");
|
||||
assert.equal(parent.normalized_result.output_truncated, false);
|
||||
if (values.provider === "gdb") assert.equal(parent.subject, null);
|
||||
else assert.equal(parent.subject.digest.sha256, oracle.artifact_sha256);
|
||||
if (values.detail === "summary") {
|
||||
const selected = delivered.normalized_result.summary;
|
||||
const { available_counts: counts, ...facts } = selected;
|
||||
const { console, target, log, ...records } = parent.normalized_result;
|
||||
assert.deepEqual(
|
||||
values.provider === "gdb" ? facts : selected,
|
||||
values.provider === "gdb" ? records : parent.normalized_result,
|
||||
);
|
||||
if (values.provider === "gdb")
|
||||
assert.equal(counts.console_utf16_units, console.length);
|
||||
assert.equal(delivered.raw_result, null);
|
||||
}
|
||||
}
|
||||
report.status = "passed";
|
||||
} catch (error) {
|
||||
report.status = "failed";
|
||||
report.failure = String(error?.stack ?? error);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
if (sessionId)
|
||||
try {
|
||||
const closed = await call(
|
||||
"close_gdb_session",
|
||||
{ session_id: sessionId },
|
||||
"cleanup:gdb",
|
||||
);
|
||||
assert.ok(!closed.isError);
|
||||
} catch (error) {
|
||||
report.gdb_cleanup_failure = String(error);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
if (
|
||||
!(await finalizeRetainedQueryReceipt({
|
||||
report,
|
||||
client,
|
||||
transport,
|
||||
account,
|
||||
serverLog,
|
||||
serverLogFinished,
|
||||
output: values.output,
|
||||
transcript,
|
||||
}))
|
||||
)
|
||||
process.exitCode = 1;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
from pathlib import Path
|
||||
import json,re,hashlib,argparse
|
||||
parser=argparse.ArgumentParser(description='Match every native instruction byte against independently mapped ELF file ranges');parser.add_argument('--root',type=Path,required=True);args=parser.parse_args();lab=args.root;oracle=json.loads((lab/'elf-oracle.json').read_text());image=Path(oracle['path']).read_bytes();assert hashlib.sha256(image).hexdigest()==oracle['artifact_sha256'];rows=[]
|
||||
for directory in sorted(lab.iterdir()):
|
||||
bundle=directory/'evidence.json'
|
||||
if not bundle.is_file():continue
|
||||
for parent in json.loads(bundle.read_text())['records']:
|
||||
if parent['operation'] not in ['gdb_console','inspect_with_objdump']:continue
|
||||
params=parent['parameters'];gdb=parent['operation']=='gdb_console'
|
||||
symbol=params.get('command','').removeprefix('disassemble /r ') if gdb else params.get('symbol')
|
||||
if symbol not in {s['name'] for s in oracle['symbols']}:continue
|
||||
spec=next(s for s in oracle['symbols'] if s['name']==symbol)
|
||||
report=parent['normalized_result'];assert report['completion_status']=='complete' and not report['output_truncated']
|
||||
if gdb:
|
||||
assert parent['subject'] is None
|
||||
console='\n'.join(line for line in report['records'] if line.startswith('~'));assert console==report['console']
|
||||
text=''.join(json.loads(line[1:]) for line in report['records'] if line.startswith('~'))
|
||||
pattern=r'^\s*(?:=>\s*)?0x([0-9a-f]+)[^:]*:\s*((?:[0-9a-f]{2}(?:\s+|$))+)'
|
||||
else:
|
||||
assert parent['subject']['digest']['sha256']==oracle['artifact_sha256'];text=report['stdout'];pattern=r'^\s*([0-9a-f]+):\s*((?:[0-9a-f]{2}(?:\s+|$))+)'
|
||||
address=spec['address'];collected=bytearray();count=0
|
||||
for line in text.splitlines():
|
||||
m=re.match(pattern,line)
|
||||
if not m:continue
|
||||
actual=int(m[1],16);raw=bytes.fromhex(m[2]);assert actual==address+len(collected),(directory,actual,address+len(collected));collected.extend(raw);count+=1
|
||||
expected=image[spec['file_offset']:spec['file_offset']+spec['bytes']];assert collected==expected,(directory,len(collected),len(expected));rows.append({'case':directory.name,'mode':'gdb' if gdb else 'objdump','symbol':symbol,'byte_bearing_lines':count,'bytes':len(collected),'exact_file_bytes':True,'parent_evidence_id':parent['evidence_id']})
|
||||
assert rows
|
||||
(lab/'native-final-verification.json').write_text(json.dumps({'status':'passed','artifact_sha256':oracle['artifact_sha256'],'records':rows},indent=2)+'\n');print(json.dumps(rows))
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,139 @@
|
||||
# Retained command observations
|
||||
|
||||
Addresses [#5584](https://github.com/morluto/rea/issues/5584). GDB command Evidence
|
||||
repeats ordered MI records in stream groups and a raw mirror; objdump repeats
|
||||
stdout/stderr in its raw mirror. Explicit `detail: summary` retains the complete
|
||||
parent and projects existing owner facts once. Default complete Evidence is
|
||||
unchanged. This adds no tool, MI decoder, live-state cache or command authority.
|
||||
|
||||
GDB summaries preserve **every ordered record**, including asynchronous and
|
||||
unrecognized records, correlated MI result, command, completion/truncation,
|
||||
invalid-UTF-8 and process-exit facts. They omit the convenience console/target/log
|
||||
groupings and report their original UTF-16 sizes. Objdump summaries preserve
|
||||
opaque stdout/stderr, exit/signal, completion and pre/post digest-change facts.
|
||||
Both carry source parameters/provider/profile, parent identity, limitations and
|
||||
unknowns. Subjectless observations stay subjectless. Historical observations do
|
||||
not establish current target state or global coverage. The existing
|
||||
`inspect_analysis_view` summary operation works after debugger close.
|
||||
|
||||
The domain codec now owns the command fact shapes used by contracts and GDB's
|
||||
capture type; protocol interpretation stays with its provider. MCP requires
|
||||
retention before command execution. CLI uses the existing shared projection and
|
||||
nonoverwriting bundle publisher after provider cleanup:
|
||||
|
||||
```sh
|
||||
rea inspect-with-objdump /usr/bin/bash disassemble --symbol execute_command \
|
||||
--detail summary --evidence_output /tmp/bash-command-evidence.json --json
|
||||
```
|
||||
|
||||
## Real input and correctness
|
||||
|
||||
Linux x86-64, Node 24.18.0/npm 11.16.0; GNU GDB 13.1 (Debian 13.1-3),
|
||||
GNU objdump 2.40, installed Bash ELF 1,265,648 bytes, SHA-256
|
||||
`55b89ab22bee4792a210f493a53fb066accd5d30b69837c28d98be5ff863efcf`.
|
||||
The [receipt](command-observation-views.json) pins native binaries, baseline main
|
||||
`c2858d2aa0916f0dd2d073d848b8c2195d9bf6b3`, source patch and packaged runtime digests.
|
||||
No target execution, attach, GUI or database mutation occurred.
|
||||
|
||||
An [independent ELF reader](command-observation-oracle.py) maps dynamic symbols
|
||||
to exact file ranges: `execute_command`, 239 bytes at `0x4ba50`, and
|
||||
`execute_command_internal`, 15,428 bytes at `0x47e00`. The
|
||||
[verifier](command-observation-verify.py) checks every disassembled byte and its
|
||||
contiguous address against those ranges. GDB reports 66/3,365 instructions;
|
||||
objdump byte lines also include continuations, so their count is not an
|
||||
instruction count. Ordered summary facts match each exported complete parent.
|
||||
GDB Evidence has a null subject; the external oracle does not manufacture a
|
||||
target binding. Actual CLI default objdump Evidence matches the baseline exactly,
|
||||
including identity; symbol/pathless summaries preserve it in bundles. CLI checks
|
||||
also cover rejected overwrite, missing destination before dispatch, and a real
|
||||
GDB command with a caller-owned PTY that remains available after debugger exit.
|
||||
|
||||
## Whole tasks and resource costs
|
||||
|
||||
The [SDK driver](command-observation-queries.mjs) discovers all 210 tools, reads
|
||||
architecture and symbol-scoped disassembly, closes GDB where applicable, exports
|
||||
complete Evidence and finalizes owned resources. GDB uses 8 calls; objdump uses 3.
|
||||
Warm cases repeat disassembly in the same process, adding one call. Fresh-process
|
||||
cases have **unflushed OS caches**, not disk-cold inputs. Native commands are
|
||||
rerun; results are not reused as current live state.
|
||||
|
||||
The [actual Codex 0.163 app-server driver](command-observation-wire.py) uses a
|
||||
loopback deterministic SSE fixture, no AI inference or paid service. All 210
|
||||
function schemas are present in every normal model-facing request. Matched tasks
|
||||
use full input/output profiles; only disassembly selects summary. The
|
||||
[counter](command-observation-costs.py) counts compact JSON serialization with
|
||||
`tiktoken 0.12.0 / cl100k_base` across **every** request, including schemas,
|
||||
replayed results and the actual client's local compaction request. The fixture
|
||||
answers compaction with observed identifiers and the retained-body location.
|
||||
These are serialization-token proxies, not model rendering, acceptance, answer
|
||||
quality, actual usage or billing. Compaction behavior is qualified only for this
|
||||
client/configuration; no inference quality is established.
|
||||
|
||||
| Completed GDB task | JSON token proxy before → after | Requests | Wall seconds | CPU seconds | Family peak MiB |
|
||||
| ------------------------------ | ------------------------------: | -------: | ------------: | ------------: | --------------: |
|
||||
| Small, 30,000 per-tool budget | 605,133 → 580,070 | 9 → 9 | 7.906 → 8.618 | 7.195 → 7.832 | 788.9 → 785.9 |
|
||||
| Large, 350,000 per-tool budget | 1,269,405 → 1,091,290 | 10 → 9 | 9.551 → 9.127 | 8.832 → 8.370 | 805.2 → 770.8 |
|
||||
|
||||
Whole-task context proxies decrease **4.14% / 14.03%**, rather than the much
|
||||
larger reply-byte reduction. The large baseline makes one compaction request;
|
||||
the candidate makes none. The small task is slower. These single paired samples
|
||||
do not establish a general speedup. With the client's default output budget,
|
||||
the small baseline delivers truncated invalid inner JSON and does **not**
|
||||
complete the question; summary completes it. That incomplete baseline is
|
||||
excluded from the percentage comparison.
|
||||
|
||||
| SDK task | Reply bytes before → after | Whole wall seconds | CPU seconds | Family peak MiB |
|
||||
| ------------- | -------------------------: | -----------------: | ------------: | --------------: |
|
||||
| GDB small | 54,123 → 18,009 | 6.631 → 5.447 | 6.102 → 5.240 | 536.3 → 535.8 |
|
||||
| GDB large | 2,763,983 → 700,453 | 6.965 → 6.660 | 6.729 → 6.451 | 595.0 → 545.8 |
|
||||
| Objdump small | 22,467 → 15,990 | 7.599 → 7.699 | 7.110 → 7.264 | 527.8 → 532.5 |
|
||||
| Objdump large | 964,889 → 487,219 | 7.737 → 7.499 | 7.334 → 6.935 | 537.0 → 536.6 |
|
||||
|
||||
Same-process repeat disassembly takes 1,100 → 1,016 ms for GDB and
|
||||
1,070 → 939 ms for objdump in these observations. Whole warm-task CPU improves
|
||||
7.613 → 7.162 s for GDB but increases 7.819 → 7.859 s for objdump. Timings include
|
||||
existing ownership checks; no check was removed or cached.
|
||||
|
||||
Retaining a summary also retains an additional derived record: large GDB bundles
|
||||
grow 1,310,073 → 1,640,022 bytes; objdump 474,288 → 712,656. This trades retained
|
||||
storage for smaller delivery; long-session heap growth is unmeasured. Retained-data lifetime follows the existing investigation ledger and cleanup
|
||||
owners. Output still scales
|
||||
with the requested complete disassembly; summary is not a fixed-size preview.
|
||||
|
||||
Each experiment runs in one owned systemd scope, one CPU, no swap, 4 GiB hard /
|
||||
3 GiB early memory thresholds and host-memory admission/stop thresholds. Recorded
|
||||
payload exit and cleanup are verified, with zero OOM/resource events. Final
|
||||
whole-scope counters disappear after teardown and remain explicitly unverified.
|
||||
The receipt includes CPU time, wall time, peak family memory, warm cases and
|
||||
bundle growth. Build/check scopes use separate bounded budgets. Host-memory
|
||||
check stops and initial fixture failures are preserved locally and are not passes.
|
||||
|
||||
## Coverage and reproduction
|
||||
|
||||
320 focused tests cover the new SDK schemas/views plus existing GDB queue,
|
||||
malformed output, cancellation, process exit, target identity and cleanup cases,
|
||||
objdump symbol selection and command admission. Real provider evidence is from
|
||||
the CLI/SDK/client runs above, not simulated-provider tests.
|
||||
|
||||
Run the oracle for the selected Bash image, then use the SDK driver with
|
||||
`--runtime`, `--input`, `--oracle`, `--output`, `--provider gdb|objdump`,
|
||||
`--symbol`, `--detail complete|summary` and optional `--warm`. The wire driver
|
||||
also takes the fixed Codex/Node paths, `--mode`, `--symbol` and `--budget`; it always performs the
|
||||
export (eight tool calls). Run each through `resource-run.py --observe-payload-exit`, preserving
|
||||
its sibling `.log`; the verifier and counter accept `--root` for those results.
|
||||
|
||||
Default Rizin execution returned ENOENT with no configured alternative. Cutter
|
||||
reported no live bridge; no configured selector or default bridge directory was
|
||||
present. Real Rizin/Cutter performance and GUI workflows remain unverified.
|
||||
Their command semantics were not changed. Existing objdump native symbol
|
||||
selection was reused. No claim covers arbitrary GDB target execution or another
|
||||
host client's output budget.
|
||||
|
||||
Review follow-up adds nine CLI publication regressions (16 CLI tests including the
|
||||
existing cleanup cases), three deterministic delivery-oracle tests, and a focused
|
||||
strict typecheck of the new CLI boundary and its transitive source. Real client
|
||||
small/default and large/generous summary runs pass the strengthened oracle and
|
||||
export complete parents; only the explicit complete/default baseline may finish
|
||||
with an incomplete answer. The unused export switch was removed: export is
|
||||
mandatory in this question. Product source and the original paired measurements
|
||||
are unchanged; the receipt records these additional validation runs separately.
|
||||
@@ -0,0 +1,301 @@
|
||||
"""Actual Codex wire capture with a deterministic local SSE backend; no AI inference."""
|
||||
import argparse
|
||||
import http.server
|
||||
import threading
|
||||
import asyncio
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import time
|
||||
import traceback
|
||||
import subprocess
|
||||
from command_observation_delivery import verify_command_delivery
|
||||
|
||||
|
||||
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")
|
||||
parser.add_argument("--input", type=Path, required=True)
|
||||
parser.add_argument("--oracle", type=Path, required=True)
|
||||
parser.add_argument("--expected-tools", type=int, default=210)
|
||||
parser.add_argument("--mode", choices=["complete", "summary"], required=True)
|
||||
parser.add_argument("--question", choices=["disassembly"], default="disassembly")
|
||||
parser.add_argument("--symbol", choices=["execute_command", "execute_command_internal"], required=True)
|
||||
parser.add_argument("--budget", type=int, default=30000)
|
||||
args = parser.parse_args()
|
||||
oracle=json.loads(args.oracle.read_text())
|
||||
target=str(args.input)
|
||||
assert args.input.is_absolute() and args.oracle.is_absolute() and args.output.is_absolute()
|
||||
assert hashlib.sha256(args.input.read_bytes()).hexdigest()==oracle["artifact_sha256"]
|
||||
def delivered(pieces):
|
||||
assert isinstance(pieces,str) and pieces.startswith("Wall time: "), pieces[:300]
|
||||
_,sep,payload=pieces.partition("Output:\n");assert sep
|
||||
try: value=json.loads(payload)
|
||||
except json.JSONDecodeError: return {"_invalid_json_payload":payload}
|
||||
return value["result"] if "result" in value and "normalized_result" not in value else value
|
||||
def outputs_of(body):
|
||||
return {item["call_id"]:delivered(item["output"]) for item in body.get("input",[]) if item.get("type")=="function_call_output"}
|
||||
steps=8
|
||||
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)
|
||||
captured = []
|
||||
known_outputs = {}
|
||||
state = {"normal_requests":0,"compactions":0}
|
||||
class Capture(http.server.BaseHTTPRequestHandler):
|
||||
def log_message(self, *_):
|
||||
pass
|
||||
def do_POST(self):
|
||||
size = int(self.headers["Content-Length"])
|
||||
assert 0 < size <= 16 * 1024 * 1024
|
||||
assert self.headers.get("Content-Encoding", "identity") == "identity"
|
||||
body = json.loads(self.rfile.read(size))
|
||||
captured.append({"path": self.path, "body": body})
|
||||
(args.output / "model-requests.json").write_text(json.dumps(captured) + "\n")
|
||||
http_index = len(captured)
|
||||
known_outputs.update(outputs_of(body))
|
||||
names = [t.get("name", "") for ns in body.get("tools", []) if ns.get("type") == "namespace" and ns.get("name") == "mcp__rea_audit" for t in ns.get("tools", [])]
|
||||
if not names:
|
||||
last=body.get('input',[])[-1]
|
||||
assert 'CONTEXT CHECKPOINT COMPACTION' in json.dumps(last)
|
||||
state['compactions']+=1;assert state['compactions']<=1
|
||||
evidence=known_outputs['call_local_6'];assert 'normalized_result' in evidence
|
||||
captured_result=evidence['normalized_result'];command_report=captured_result.get('summary',captured_result)
|
||||
checkpoint={'fixture':'deterministic protocol checkpoint; no AI inference or model-quality claim','task':'Full native Bash function disassembly has been delivered to this compaction request. Retain complete Evidence and finish owned cleanup.','path':target,'artifact_sha256_external_oracle':oracle['artifact_sha256'],'symbol':args.symbol,'session_id':known_outputs['call_local_1']['session_id'],'parent_evidence_id':captured_result.get('parent_evidence_id',evidence['evidence_id']),'subject':evidence['subject'],'completion_status':command_report['completion_status'],'output_truncated':command_report['output_truncated'],'native_console_utf16_units':command_report.get('available_counts',{}).get('console_utf16_units',len(command_report.get('console','').encode('utf-16-le'))//2),'full_body_location':'same-session retained Evidence; export next to '+str(args.output/'evidence.json'),'remaining':['close_gdb_session','export_evidence_bundle']}
|
||||
text=json.dumps(checkpoint)
|
||||
item={"type":"message","id":f"msg_checkpoint_{state['compactions']}","role":"assistant","status":"completed","content":[{"type":"output_text","text":text,"annotations":[]}]}
|
||||
captured[-1]['kind']='deterministic-compaction'
|
||||
else:
|
||||
state['normal_requests']+=1;index=state['normal_requests'];assert index<=steps+1
|
||||
captured[-1]['kind']='tool-planning-or-final'
|
||||
if index<=steps:
|
||||
if index==1:
|
||||
operation="start_gdb_session";arguments={}
|
||||
else:
|
||||
session=known_outputs["call_local_1"]["session_id"]
|
||||
if index<=6:
|
||||
command=['set auto-load off','set debuginfod enabled off',f'file {target}','show architecture',f'disassemble /r {args.symbol}'][index-2]
|
||||
operation="gdb_console";arguments={"session_id":session,"command":command}
|
||||
if index==6 and args.mode=="summary":arguments["detail"]="summary"
|
||||
elif index==7:
|
||||
operation="close_gdb_session";arguments={"session_id":session}
|
||||
else:
|
||||
operation="export_evidence_bundle";arguments={"path":str(args.output/'evidence.json')}
|
||||
selected=[name for name in names if name.endswith(operation)];assert len(selected)==1
|
||||
item={"type":"function_call","id":f"fc_local_{index}","call_id":f"call_local_{index}","name":selected[0],"namespace":"mcp__rea_audit","arguments":json.dumps(arguments),"status":"completed"}
|
||||
else:
|
||||
item={"type":"message","id":"msg_local_done","role":"assistant","status":"completed","content":[{"type":"output_text","text":"Scripted local GDB disassembly task complete.","annotations":[]}]}
|
||||
response_id=f"resp_local_{http_index}"
|
||||
base = {"id": response_id, "object": "response", "model": body.get("model"), "status": "completed", "output": [item]}
|
||||
events = [
|
||||
{"type": "response.created", "response": {**base, "status": "in_progress", "output": []}},
|
||||
{"type": "response.output_item.added", "output_index": 0, "item": {**item, "status": "in_progress"}},
|
||||
{"type": "response.output_item.done", "output_index": 0, "item": item},
|
||||
{"type": "response.completed", "response": base},
|
||||
]
|
||||
wire = "".join("event: " + e["type"] + "\ndata: " + json.dumps(e) + "\n\n" for e in events).encode()
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "text/event-stream")
|
||||
self.send_header("Content-Length", str(len(wire)))
|
||||
self.end_headers()
|
||||
self.wfile.write(wire)
|
||||
endpoint = http.server.HTTPServer(("127.0.0.1", 0), Capture)
|
||||
endpoint_thread = threading.Thread(target=endpoint.serve_forever, daemon=True)
|
||||
endpoint_thread.start()
|
||||
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 = 'model_provider = "rea_capture"\nmodel = "rea-wire-probe"\n[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())
|
||||
for name in ['start_gdb_session','gdb_console','close_gdb_session','export_evidence_bundle']:
|
||||
config += f'[mcp_servers.rea_audit.tools.{name}]\napproval_mode = "approve"\n'
|
||||
if args.budget: config += f'output_token_limit = {args.budget}\n'
|
||||
config += f'\n[model_providers.rea_capture]\nname = "Local scripted capture"\nbase_url = "http://127.0.0.1:{endpoint.server_port}/v1"\nwire_api = "responses"\nrequires_openai_auth = false\nsupports_websockets = false\nrequest_max_retries = 0\nstream_max_retries = 0\n'
|
||||
(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": "Return architecture and complete byte-annotated disassembly for one selected native Bash function and export complete Evidence. No target execution.",
|
||||
"client": "Codex app-server 0.163.0", "model_turns_started": 1, "backend": "local deterministic SSE fixture; no AI model or paid service",
|
||||
"actual_model_usage": None, "input_profile": args.input_profile,
|
||||
"output_profile": args.output_profile, "calls": [], "observations": [],
|
||||
"unexpected_server_requests": [], "cleanup_failures": []}
|
||||
client_version = subprocess.check_output([str(args.codex), "--version"], env=environment, cwd=workspace, text=True, timeout=10).strip()
|
||||
assert client_version == "codex-cli 0.163.0", client_version
|
||||
transcript = []
|
||||
pending = {}
|
||||
completed_turn = asyncio.Event()
|
||||
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 value.get("method") == "turn/completed":
|
||||
report["turn"] = value["params"]["turn"]
|
||||
completed_turn.set()
|
||||
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 scripted metadata 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", "turn/start"]
|
||||
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)) == args.expected_tools
|
||||
assert "gdb_console" in names
|
||||
output_schemas = sum(tool.get("outputSchema") is not None for tool in tools)
|
||||
assert output_schemas == (args.expected_tools 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")}
|
||||
await request("turn/start", {"threadId":thread_id,"input":[{"type":"text","text":f"Read architecture and the complete byte-annotated disassembly of {args.symbol} in the selected Bash executable. Retain and export complete Evidence, then close the owned debugger. The local fixture scripts the required tools; no AI inference or target execution is used."}]})
|
||||
await asyncio.wait_for(completed_turn.wait(), 90)
|
||||
assert report["turn"]["status"] == "completed", report["turn"]
|
||||
assert state["normal_requests"]==steps+1
|
||||
assert len(captured)==steps+1+state["compactions"]
|
||||
outputs=known_outputs
|
||||
assert len(outputs)==steps
|
||||
bundle=json.loads((args.output/'evidence.json').read_text())
|
||||
evidence=next(e for e in bundle['records'] if e['operation']=='gdb_console' and e['parameters']['command']==f'disassemble /r {args.symbol}')
|
||||
assert evidence['subject'] is None
|
||||
assert evidence['normalized_result']['completion_status']=='complete' and not evidence['normalized_result']['output_truncated']
|
||||
delivered_result=outputs['call_local_6']
|
||||
delivery_complete=verify_command_delivery(delivered_result,evidence,args.mode,args.budget)
|
||||
if delivery_complete and args.mode=='summary':
|
||||
assert next(e for e in bundle['records'] if e['evidence_id']==delivered_result['evidence_id'])==delivered_result
|
||||
report['task_completed']=delivery_complete
|
||||
report['observations']={'parent_evidence_id':evidence['evidence_id'],'exported':True,'native_console_utf16_units':len(evidence['normalized_result']['console'].encode('utf-16-le'))//2,'client_delivered_full_evidence':delivery_complete,'selected_symbol':args.symbol,'tool_output_budget':args.budget or 'client default','subject':evidence['subject']}
|
||||
report["captured_requests"]=len(captured)
|
||||
report["compaction_requests"]=state["compactions"]
|
||||
report["compaction_backend"]="Deterministic checkpoint of observed identifiers and retained-body location; no AI inference or quality claim."
|
||||
report["mode"]=args.mode
|
||||
report["request_tool_counts"] = [len(c["body"].get("tools", [])) for c in captured]
|
||||
assert not report["unexpected_server_requests"]
|
||||
assert hashlib.sha256(args.input.read_bytes()).hexdigest()==oracle["artifact_sha256"]
|
||||
report["status"] = "passed" if report["task_completed"] else "expected-incomplete-task"
|
||||
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")
|
||||
endpoint.shutdown()
|
||||
endpoint.server_close()
|
||||
endpoint_thread.join(5)
|
||||
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"] in ["passed","expected-incomplete-task"] and not report["cleanup_failures"] else 1
|
||||
|
||||
|
||||
raise SystemExit(asyncio.run(main()))
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Acceptance of the captured GDB answer, separate from native execution success."""
|
||||
|
||||
|
||||
def verify_command_delivery(delivered, parent, mode, budget):
|
||||
assert mode in ('complete', 'summary')
|
||||
if '_invalid_json_payload' in delivered:
|
||||
assert mode == 'complete' and budget == 0, (
|
||||
'Incomplete delivery is allowed only for the '
|
||||
'complete/default-budget baseline'
|
||||
)
|
||||
return False
|
||||
if mode == 'complete':
|
||||
assert delivered == parent
|
||||
return True
|
||||
|
||||
selected = delivered['normalized_result']
|
||||
assert selected['parent_evidence_id'] == parent['evidence_id']
|
||||
assert selected['artifact'] is None
|
||||
profile = parent['analysis_profile']
|
||||
expected_profile = None if profile is None else profile['digest']
|
||||
assert selected['source_analysis_profile_digest'] == expected_profile
|
||||
assert selected['source_parameters'] == parent['parameters']
|
||||
assert selected['source_provider'] == parent['provider']
|
||||
assert delivered['raw_result'] is None and delivered['subject'] is None
|
||||
expected = {
|
||||
key: value for key, value in parent['normalized_result'].items()
|
||||
if key not in ('console', 'target', 'log')
|
||||
}
|
||||
actual = {
|
||||
key: value for key, value in selected['summary'].items()
|
||||
if key != 'available_counts'
|
||||
}
|
||||
assert actual == expected
|
||||
counts = {'records': len(expected['records'])}
|
||||
for name in ('console', 'target', 'log'):
|
||||
text = parent['normalized_result'][name]
|
||||
counts[name + '_utf16_units'] = len(text.encode('utf-16-le')) // 2
|
||||
assert selected['summary']['available_counts'] == counts
|
||||
return True
|
||||
@@ -433,6 +433,38 @@
|
||||
"members": 315,
|
||||
"expanded_bytes": 1301927
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "alpine-ripgrep-rust",
|
||||
"file": "ripgrep",
|
||||
"bytes": 3416168,
|
||||
"sha256": "78387bbe12b2110df8d2ce364390a00c190f4543a2f40b8279e49f6c5c85af9c",
|
||||
"provenance": {
|
||||
"project": "ripgrep",
|
||||
"version": "14.1.1-r0",
|
||||
"upstreamFile": "ripgrep-14.1.1-r0.apk",
|
||||
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.21/community/x86_64/ripgrep-14.1.1-r0.apk",
|
||||
"transport_sha256": "213dd0b047163f6627cc2fdd6fbfe205009476eb4fc48c352a3f5b9ab647f6d4",
|
||||
"transport_bytes": 1422830,
|
||||
"aports_revision": "4e899d119e4c983a938436d6f397a509521225d1",
|
||||
"derivation": "selected regular APK tar member usr/bin/rg; read-only static inspection; never executed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "alpine-zellij-rust",
|
||||
"file": "zellij",
|
||||
"bytes": 17151864,
|
||||
"sha256": "9a86e9ca47902a869fdf1886f4cf17714f4fa5655427128beadff498cfb392db",
|
||||
"provenance": {
|
||||
"project": "zellij",
|
||||
"version": "0.40.0-r0",
|
||||
"upstreamFile": "zellij-0.40.0-r0.apk",
|
||||
"url": "https://dl-cdn.alpinelinux.org/alpine/v3.21/community/x86_64/zellij-0.40.0-r0.apk",
|
||||
"transport_sha256": "575075945415b221857563e19c5f4c661af9f98bbba54f0994c7385c0e432432",
|
||||
"transport_bytes": 6722129,
|
||||
"aports_revision": "3be26a3a26e76e1bf3eff784bffe3956d29d77a0",
|
||||
"derivation": "selected regular APK tar member usr/bin/zellij; read-only static inspection; never executed"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
# Workflow coverage reconciliation, 2026-10-21
|
||||
|
||||
The checked snapshot is main `264862bf2`, with **46 first-level `src/`
|
||||
directories and 212 production stdio MCP tools**. Directory counts and tool
|
||||
counts are different inventories: one application workflow can cross several
|
||||
directories, and one provider can expose many independently costly operations.
|
||||
This is a source/history review, not a new runtime qualification of all tools.
|
||||
|
||||
The search includes this experiment directory, owning contracts and source
|
||||
history, original feature PRs and related open/closed issues. Missing experiment
|
||||
files do not establish missing audits. In particular, the model and IL2CPP
|
||||
feature PRs contain substantial real-task evidence absent from this directory.
|
||||
Historical measurements retain their original revisions, platforms and limits.
|
||||
|
||||
| Directories / workflow | Evidence already present | Remaining coverage boundary |
|
||||
| ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `kuna`, `ghidra` | Earlier native entry work and [Ghidra projection](ghidra-evidence-projection.md) | Selected questions only; database reuse is a separate [research issue #3136](https://github.com/morluto/rea/issues/3136). |
|
||||
| `ida` | [Windows native correctness and completed-task costs](ida-native-windows.md), including real `whoami.exe` | Warm selected-task gains do not establish cold analysis, all string formats, all operations or other IDA platforms. |
|
||||
| `hopper`, `jeb` | [Hopper string records](hopper-string-records.md), [Linux runtime](hopper-linux-python-runtime.md), [real JEB CE](jeb-linux-native.md) | Preserve negative whole-task results; other editions, platforms and workflows are separate scopes. |
|
||||
| `rizin`, `cutter` | Existing command/session adapters and lifecycle fixes | No comparable dedicated real completed-task performance record located in the searched records. Actual engine/GUI availability must be established before selecting a task. |
|
||||
| `gdb`, `objdump` | [Real Bash command observations](command-observation-views.md) | Selected read-only disassembly is measured; other commands, large outputs and running-target observation are separate tasks. |
|
||||
| `adb`, `frida`, `inspector` | [Frida native status/history experiment](frida-status-views.md) | Frida status does not qualify arbitrary scripts, ADB/device transfers, browser Inspector events or live target freshness. |
|
||||
| `go`, `rust` | [Go build/dependency selection on real binaries](go-build-views.md); Rust cargo-auditable support [#2456](https://github.com/morluto/rea/pull/2456) | Go is already measured. No comparable dedicated Rust dependency-investigation cost record located in this review. |
|
||||
| `javascript`, `browser`, `electron` | [Agent fact workflow](javascript-facts-agent-history.json), lifecycle experiments and [#4415](https://github.com/morluto/rea/issues/4415) | Real Joplin preparation remains exploratory. Individual JS features and browser/runtime workflows need their own evidence. |
|
||||
| `android`, `apktool` | [Package summary](android-package-summary.md), [three-APK shared resource decoding](apktool-string-tables.md) | These do not qualify all JADX class/method/reference operations or physical-device workflows. #4777 is already fixed. |
|
||||
| `dotnet`, `il2cpp` | [Managed summaries/pages](managed-artifact-summary.md), IL2CPP metadata [#3807](https://github.com/morluto/rea/pull/3807) and native links/seeds [#5327](https://github.com/morluto/rea/pull/5327) | IL2CPP already has real Windows/Android row comparisons, task costs and Ghidra import. Unsupported versions/generics/platforms remain separate correctness and coverage work. |
|
||||
| `model` | GGUF/safetensors [#5603](https://github.com/morluto/rea/pull/5603): real models, independent readers and complete-task costs | Existing delivery is already audited. [#5774](https://github.com/morluto/rea/issues/5774) isolates omitted-string validation cost within that producer. |
|
||||
| `native`, `artifacts` | [Native inventory](native-inventory-views.md), [entry views](native-entry-views.md); real NIB output reductions [#1460](https://github.com/morluto/rea/pull/1460), [#1477](https://github.com/morluto/rea/pull/1477) | Inventory does not qualify Apple UI/resource, signatures, dyld or runtime traces. NIB byte reductions alone do not establish token, time, CPU or memory savings for a completed question. |
|
||||
| `firmware` | Region pilot plus real full OpenWrt extraction/publication [#4602](https://github.com/morluto/rea/pull/4602) | Extraction is already measured; noisy timings establish no general speedup. Other image layouts, extraction trees and publication filesystems remain unqualified. |
|
||||
| `flutter`, `sqlite`, `wasm`, `evm`, `pcap`, `signals` | [Dart AOT](flutter-aot-summary.md), [SQLite controls](sqlite-existing-controls.md), [WAT framing](wasm-string-framing.md), [EVM](evm-interface-summary.md), [PCAP](pcap-small-captures.md), [signals](signal-selection-workflows.md) | Extend a specific question. Preserve cases where existing controls suffice and small tasks regress; these records do not qualify each format or tool exhaustively. |
|
||||
| `application`, `domain`, `server`, `cli`, `contracts` | [Mixed retained investigations](retained-investigation-workflows.md), [immutable parent validation](immutable-parent-validation.md), [capture validation](retained-capture-validation.md) | Shared selected-query paths are already optimized. Reconstruction/environment composition and new fact families require their own end-to-end questions. |
|
||||
| `browser` / recorded captures | [SAZ selection](saz-selection-workflows.md) and [real qlog producer/retained reads](retained-capture-validation.md) | [#3133](https://github.com/morluto/rea/issues/3133) still includes actual model/task questions; producer measurements do not close those requirements. |
|
||||
| `filesystem`, `process`, `reference`, `providers`, `captures`, `windows`, `main`, `composition`, `config`, `evaluation` | Cross-workflow resource/cleanup checks, [process traces](process-trace-validation.md), [MCP profile](mcp-profile-identity.md) and Windows lifecycle checkpoints | Infrastructure coverage follows the exercised workflow. No blanket startup/resource claim; reference imports and `compare_source_to_bundle` need a dedicated completed source-tree question. |
|
||||
|
||||
Every one of the 46 directories appears above; repeated `browser` highlights a
|
||||
distinct workflow, not another directory. The table records located evidence,
|
||||
not proof that every historical PR was inspected. Open issue state by itself
|
||||
does not prove missing implementation or absent measurements.
|
||||
|
||||
## Three bounded follow-up questions
|
||||
|
||||
1. **Rust dependency identity:** inspect a public cargo-auditable binary, find
|
||||
exact dependency/version edges and retain complete source Evidence. Compare
|
||||
existing delivery first; independently decode its auditable section. Include
|
||||
a small binary and account for all discovery, queries and serialization.
|
||||
2. **Apple UI graph lookup:** on macOS, locate a specific control/outlet in a
|
||||
real Transmission/XLD NIB graph using existing summary/document pages.
|
||||
Compare equivalent complete questions and independently check archive
|
||||
records. Historical byte reductions alone are not the completion criterion.
|
||||
3. **Reference/source comparison:** import a pinned public source tree and
|
||||
answer one source-to-bundle identity question through existing workflows.
|
||||
Independently check file manifests, symlink/unknown semantics and selected
|
||||
matches; measure repeated traversal and retained representation before
|
||||
proposing an index or cache.
|
||||
|
||||
Each lane stops with a measured benefit, an evidenced no-change decision, or an
|
||||
explicit unavailable prerequisite. Rizin/Cutter and device/live workflows stay
|
||||
visible as additional gaps; no new execution, injection or mutation authority is
|
||||
implied by this review. No all-provider run or new API is justified by counts.
|
||||
@@ -0,0 +1,81 @@
|
||||
"""Count complete actual-client requests, not only smaller tool-reply bytes."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import tiktoken
|
||||
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--lab', type=Path, required=True)
|
||||
parser.add_argument('--output', type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
enc = tiktoken.get_encoding('cl100k_base')
|
||||
|
||||
|
||||
def tokens(value):
|
||||
return len(enc.encode(json.dumps(value, ensure_ascii=False, separators=(',', ':'), allow_nan=False), disallowed_special=()))
|
||||
|
||||
|
||||
def resources(root):
|
||||
receipt = json.loads(Path(str(root) + '.log').read_text().splitlines()[-1])
|
||||
payload = receipt['payload_resource_observation']
|
||||
assert receipt['abort'] is None and receipt['payload_resource_observation_verified']
|
||||
assert payload['payload_exit_code'] == 0 and payload['payload_cleanup_verified']
|
||||
for events in payload['resource_events'].values():
|
||||
assert not any(events.values())
|
||||
return {'wall_seconds': receipt['elapsed_seconds'], 'cpu_seconds': payload['cpu_stat']['usage_usec'] / 1e6,
|
||||
'family_peak_mib': payload['memory_peak_bytes'] / 1048576,
|
||||
'payload_cleanup_verified': True, 'final_whole_scope_counters_verified': receipt['resource_counters_verified']}
|
||||
|
||||
|
||||
wire, sdk = [], []
|
||||
for root in sorted(args.lab.glob('wire-v2-*')):
|
||||
if not root.is_dir():
|
||||
continue
|
||||
report = json.loads((root / 'report.json').read_text())
|
||||
assert report['status'] in ['passed', 'expected-incomplete-task'] and not report['cleanup_failures']
|
||||
assert report['appserver_exit_code'] == 0 and report['owned_scope_drain']['remaining_processes'] == []
|
||||
requests = json.loads((root / 'model-requests.json').read_text())
|
||||
details = []
|
||||
for captured in requests:
|
||||
body = captured['body']
|
||||
namespace = [n for n in body.get('tools', []) if n.get('type') == 'namespace' and n.get('name') == 'mcp__rea_audit']
|
||||
assert len(namespace) == 1 and len(namespace[0]['tools']) == 211, 'Unexpected compaction/catalog: audit before comparing'
|
||||
details.append({'whole_json_tokens_proxy': tokens(body), 'rea_namespace_tokens_proxy': tokens(namespace[0]), 'rea_functions': 211})
|
||||
wire.append({'case': root.name, 'task_completed': report['task_completed'], 'mode': report['mode'],
|
||||
'budget': report['observations']['tool_output_budget'], 'requests': len(details), 'compactions': report['compaction_requests'],
|
||||
'whole_json_tokens_proxy': sum(r['whole_json_tokens_proxy'] for r in details), 'request_details': details, **resources(root)})
|
||||
for root in sorted(args.lab.glob('sdk-*')):
|
||||
if not root.is_dir():
|
||||
continue
|
||||
report = json.loads((root / 'report.json').read_text())
|
||||
assert report['status'] == 'passed' and not report['cleanup_failures'] and not report['receipt_failures']
|
||||
transcript = json.loads((root / 'transcript.json').read_text())
|
||||
catalog = json.loads((root / 'catalog.json').read_text())
|
||||
assert len(catalog['tools']) == 211 and report['original_target_exited']
|
||||
sdk.append({'case': root.name, 'detail': report['detail'], 'capture': report['capture'], 'tool_calls': len(report['calls']),
|
||||
'exported_bundle_bytes': report['exported_bundle_bytes'], 'answer': report['answer'],
|
||||
'status_calls': [c for c in report['calls'] if c['name'] == 'frida_session_status'],
|
||||
'one_catalog_plus_all_sdk_calls_tokens_proxy': tokens(catalog) + sum(tokens({'name': t['name'], 'input': t['input'], 'reply': t['reply']}) for t in transcript), **resources(root)})
|
||||
assert len(sdk) == len(wire) == 4, 'A partial cohort cannot qualify a comparison'
|
||||
for capture in ['small', 'libc']:
|
||||
pair = [r for r in sdk if r['capture'] == capture]
|
||||
assert len(pair) == 2 and {r['detail'] for r in pair} == {'complete', 'summary'}
|
||||
assert {k: v for k, v in pair[0]['answer'].items() if k != 'pid'} == {k: v for k, v in pair[1]['answer'].items() if k != 'pid'}
|
||||
reports = [json.loads((args.lab / r['case'] / 'report.json').read_text()) for r in pair]
|
||||
assert reports[0]['load']['source_sha256'] == reports[1]['load']['source_sha256']
|
||||
completed = [r for r in wire if r['budget'] == 300000]
|
||||
assert len(completed) == 2 and all(r['task_completed'] for r in completed)
|
||||
answers = [json.loads((args.lab / r['case'] / 'report.json').read_text())['observations']['answers'] for r in completed]
|
||||
comparable_answers = [[{k: v for k, v in a.items() if k != 'parent_evidence_id'} for a in run] for run in answers]
|
||||
assert comparable_answers[0] == comparable_answers[1]
|
||||
result = {
|
||||
'scope': 'Real local Frida binding with controlled sleep target and caller-authored libc export inventory. Actual Codex0.163 app-server uses a deterministic local SSE fixture: no AI inference, model usage, quality or billing claim. Whole compact JSON token estimates include all 211 schemas and every request replay. Incomplete default baseline is excluded from completed-task gains. SDK exchange estimates are not model-input measurements. Fresh processes have unflushed OS caches; both status reads query live session observations. Timings include the eight-second target lifetime and cleanup; single pairs establish no general speedup. Payload-exit cgroup counters are verified; final whole-scope counters may be unavailable.',
|
||||
'tokenizer': {'package': tiktoken.__version__, 'encoding': enc.name},
|
||||
'runtime_pins': {v: {k: x for k, x in json.loads((args.lab / (v + '-manifest.json')).read_text()).items() if k != 'files'} for v in ['baseline', 'candidate']},
|
||||
'native': json.loads((args.lab / 'native-prerequisite-manifest.json').read_text()),
|
||||
'script_pins': {p.name: hashlib.sha256(p.read_bytes()).hexdigest() for p in Path(__file__).parent.glob('frida*') if p.suffix in ['.py', '.mjs']},
|
||||
'wire': wire, 'sdk': sdk,
|
||||
}
|
||||
args.output.write_text(json.dumps(result, indent=2) + '\n')
|
||||
print(json.dumps({'wire': [{k: v for k, v in r.items() if k != 'request_details'} for r in wire], 'sdk': [{k: v for k, v in r.items() if k not in ['status_calls', 'answer']} for r in sdk]}, indent=2))
|
||||
@@ -0,0 +1,267 @@
|
||||
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 { createRequire } from "node:module";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { finished } from "node:stream/promises";
|
||||
import { parseArgs } from "node:util";
|
||||
import { createRetainedQueryCaller } from "./retained-query-calls.mjs";
|
||||
import { finalizeRetainedQueryReceipt } from "./retained-query-finalization.mjs";
|
||||
const { values } = parseArgs({
|
||||
options: {
|
||||
runtime: { type: "string" },
|
||||
output: { type: "string" },
|
||||
detail: { type: "string", default: "complete" },
|
||||
capture: { type: "string", default: "libc" },
|
||||
},
|
||||
});
|
||||
const { runtime, output, detail, capture } = values;
|
||||
assert.ok(runtime && output);
|
||||
assert.ok(["complete", "summary"].includes(detail));
|
||||
assert.ok(["libc", "small"].includes(capture));
|
||||
const require = createRequire(join(runtime, "package.json"));
|
||||
const { Client } = await import(
|
||||
pathToFileURL(require.resolve("@modelcontextprotocol/client"))
|
||||
);
|
||||
const { StdioClientTransport } = await import(
|
||||
pathToFileURL(require.resolve("@modelcontextprotocol/client/stdio"))
|
||||
);
|
||||
const { parseLinuxProcessStat } = await import(
|
||||
pathToFileURL(join(runtime, "dist/process/LinuxProcessStat.js"))
|
||||
);
|
||||
await mkdir(output, { mode: 0o700 });
|
||||
const account = await mkdtemp(join(tmpdir(), "rea-frida-real-"));
|
||||
const serverLog = createWriteStream(join(output, "server.log"));
|
||||
const serverLogFinished = finished(serverLog);
|
||||
serverLogFinished.catch(() => {});
|
||||
const client = new Client({ name: "rea-frida-audit", version: "1" });
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [join(runtime, "scripts/rea.mjs"), "mcp"],
|
||||
env: {
|
||||
PATH: process.env.PATH,
|
||||
HOME: account,
|
||||
NODE_OPTIONS: "--max-old-space-size=2048",
|
||||
UV_THREADPOOL_SIZE: "1",
|
||||
REA_LOG_LEVEL: "silent",
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
transport.stderr?.pipe(serverLog, { end: false });
|
||||
const report = {
|
||||
question:
|
||||
"Retain a complete caller-requested libc export inventory, then check only session identity/state and message capture coverage twice, preserving complete Evidence.",
|
||||
detail,
|
||||
capture,
|
||||
calls: [],
|
||||
status: "pending",
|
||||
};
|
||||
const transcript = [];
|
||||
const caller = createRetainedQueryCaller(client, (receipt, event) => {
|
||||
report.calls.push(receipt);
|
||||
transcript.push(event);
|
||||
});
|
||||
const call = async (name, input) => {
|
||||
const r = await caller(name, input, name);
|
||||
assert.ok(!r.isError, JSON.stringify(r));
|
||||
return r.structuredContent;
|
||||
};
|
||||
const delay = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
let session;
|
||||
let birth;
|
||||
const stat = async () => {
|
||||
try {
|
||||
return parseLinuxProcessStat(
|
||||
await readFile(`/proc/${session.pid}/stat`, "utf8"),
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.code === "ENOENT") return null;
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
try {
|
||||
await client.connect(transport);
|
||||
const catalog = await client.listTools();
|
||||
await writeFile(join(output, "catalog.json"), JSON.stringify(catalog));
|
||||
report.catalog_tools = catalog.tools.length;
|
||||
const defs = new Map(catalog.tools.map((t) => [t.name, t]));
|
||||
const orig = client.callTool.bind(client);
|
||||
client.callTool = (input, options) =>
|
||||
orig(input, {
|
||||
...options,
|
||||
timeout: 30000,
|
||||
toolDefinition: defs.get(input.name),
|
||||
});
|
||||
report.devices = await call("list_frida_devices", {});
|
||||
session = await call("start_frida_session", {
|
||||
mode: "spawn",
|
||||
device_id: "local",
|
||||
program: "/usr/bin/sleep",
|
||||
argv: ["8"],
|
||||
});
|
||||
report.session = session;
|
||||
birth = await stat();
|
||||
assert.ok(birth);
|
||||
report.target_birth = birth;
|
||||
report.same_owned_cgroup =
|
||||
(await readFile(`/proc/${session.pid}/cgroup`, "utf8")) ===
|
||||
(await readFile("/proc/self/cgroup", "utf8"));
|
||||
assert.ok(report.same_owned_cgroup);
|
||||
const source = `send({kind:'ready',pid:Process.id});
|
||||
setTimeout(() => {
|
||||
if (${JSON.stringify(capture)} === 'small') { send({kind:'complete',total:0}); return; }
|
||||
const module = Process.enumerateModules().find(m => /(^|\\/)libc\\.so\\.6$/.test(m.path));
|
||||
if (!module) { send({kind:'missing-libc'}); return; }
|
||||
const exports = module.enumerateExports().map(e => ({type:e.type,name:e.name,address:e.address.toString()}));
|
||||
send({kind:'module',pid:Process.id,path:module.path,base:module.base.toString(),size:module.size,total:exports.length});
|
||||
for (let offset = 0; offset < exports.length; offset += 64) send({kind:'exports',offset,items:exports.slice(offset,offset+64)});
|
||||
send({kind:'complete',total:exports.length});
|
||||
}, 300);`;
|
||||
await writeFile(join(output, "selected-script.js"), source);
|
||||
report.source_sha256 = createHash("sha256").update(source).digest("hex");
|
||||
const loaded = await call("load_frida_script", {
|
||||
session_id: session.session_id,
|
||||
source_kind: "inline",
|
||||
source,
|
||||
});
|
||||
assert.equal(loaded.normalized_result.source_sha256, report.source_sha256);
|
||||
report.load = loaded.normalized_result;
|
||||
await call("resume_frida_session", { session_id: session.session_id });
|
||||
await delay(750);
|
||||
const snapshots = [];
|
||||
for (let n = 0; n < 2; n++)
|
||||
snapshots.push(
|
||||
await call("frida_session_status", {
|
||||
session_id: session.session_id,
|
||||
...(detail === "summary" ? { detail } : {}),
|
||||
}),
|
||||
);
|
||||
await call("export_evidence_bundle", { path: join(output, "evidence.json") });
|
||||
const bundle = JSON.parse(
|
||||
await readFile(join(output, "evidence.json"), "utf8"),
|
||||
);
|
||||
for (const snapshot of snapshots) {
|
||||
assert.equal(snapshot.raw_result, null);
|
||||
const parent =
|
||||
detail === "summary"
|
||||
? bundle.records.find(
|
||||
(e) =>
|
||||
e.evidence_id === snapshot.normalized_result.parent_evidence_id,
|
||||
)
|
||||
: snapshot;
|
||||
assert.ok(parent);
|
||||
assert.equal(parent.authority, "external-service");
|
||||
assert.equal(parent.environment, null);
|
||||
assert.equal(parent.raw_result, null);
|
||||
assert.equal(parent.operation, "frida_session_status");
|
||||
assert.deepEqual(parent.parameters, { session_id: session.session_id });
|
||||
const r = parent.normalized_result;
|
||||
assert.equal(r.pid, session.pid);
|
||||
assert.equal(r.state, "running");
|
||||
assert.equal(r.messages_truncated, false);
|
||||
const payloads = r.messages
|
||||
.filter((m) => m.type === "send")
|
||||
.map((m) => m.payload);
|
||||
const ready = payloads.find((p) => p?.kind === "ready");
|
||||
const complete = payloads.find((p) => p?.kind === "complete");
|
||||
assert.equal(ready?.pid, session.pid);
|
||||
assert.ok(complete);
|
||||
let count = 0;
|
||||
let header;
|
||||
if (capture === "libc") {
|
||||
header = payloads.find((p) => p?.kind === "module");
|
||||
assert.ok(header);
|
||||
assert.equal(header.pid, session.pid);
|
||||
for (const chunk of payloads.filter((p) => p?.kind === "exports")) {
|
||||
assert.equal(chunk.offset, count);
|
||||
count += chunk.items.length;
|
||||
}
|
||||
assert.equal(count, header.total);
|
||||
assert.ok(count > 1000);
|
||||
const maps = await readFile(`/proc/${session.pid}/maps`, "utf8");
|
||||
assert.ok(maps.includes(header.path));
|
||||
}
|
||||
assert.equal(count, complete.total);
|
||||
if (detail === "summary") {
|
||||
const view = snapshot.normalized_result;
|
||||
assert.equal(view.parent_operation, parent.operation);
|
||||
assert.equal(view.parent_digest, parent.evidence_id.slice(3));
|
||||
assert.equal(view.artifact, null);
|
||||
assert.deepEqual(view.source_parameters, parent.parameters);
|
||||
assert.deepEqual(view.source_provider, parent.provider);
|
||||
const { messages, ...metadata } = r;
|
||||
assert.deepEqual(view.summary, {
|
||||
...metadata,
|
||||
messages_retained: messages.length,
|
||||
});
|
||||
assert.ok(view.unknowns.length > 0);
|
||||
assert.ok(view.limitations.some((m) => m.includes("operating system")));
|
||||
assert.deepEqual(
|
||||
bundle.records.find((e) => e.evidence_id === snapshot.evidence_id),
|
||||
snapshot,
|
||||
);
|
||||
}
|
||||
report.answer = {
|
||||
pid: r.pid,
|
||||
state: r.state,
|
||||
scripts: r.scripts.length,
|
||||
messages: r.messages.length,
|
||||
messages_truncated: r.messages_truncated,
|
||||
exports: count,
|
||||
module_path: header?.path ?? null,
|
||||
};
|
||||
}
|
||||
report.native_oracle =
|
||||
"Caller-authored script digest and contiguous chunk coverage; PID/birthtime/cgroup and libc path independently checked in procfs. Export addresses and native export completeness not independently qualified.";
|
||||
report.exported_bundle_bytes = Buffer.byteLength(
|
||||
await readFile(join(output, "evidence.json")),
|
||||
);
|
||||
await call("close_frida_session", { session_id: session.session_id });
|
||||
session.closed = true;
|
||||
const deadline = performance.now() + 12000;
|
||||
while (true) {
|
||||
const current = await stat();
|
||||
if (
|
||||
current === null ||
|
||||
current.startTime !== birth.startTime ||
|
||||
["zombie", "dead"].includes(current.lifecycle)
|
||||
) {
|
||||
report.original_target_exited = true;
|
||||
break;
|
||||
}
|
||||
assert.ok(
|
||||
performance.now() < deadline,
|
||||
"Original target exit not observed",
|
||||
);
|
||||
await delay(50);
|
||||
}
|
||||
report.status = "passed";
|
||||
} catch (error) {
|
||||
report.status = "failed";
|
||||
report.failure = String(error?.stack ?? error);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
if (session && !session.closed)
|
||||
try {
|
||||
await call("close_frida_session", { session_id: session.session_id });
|
||||
} catch (error) {
|
||||
report.session_cleanup_failure = String(error);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
if (
|
||||
!(await finalizeRetainedQueryReceipt({
|
||||
report,
|
||||
client,
|
||||
transport,
|
||||
account,
|
||||
serverLog,
|
||||
serverLogFinished,
|
||||
output,
|
||||
transcript,
|
||||
}))
|
||||
)
|
||||
process.exitCode = 1;
|
||||
}
|
||||
@@ -0,0 +1,468 @@
|
||||
{
|
||||
"scope": "Real local Frida binding with controlled sleep target and caller-authored libc export inventory. Actual Codex0.163 app-server uses a deterministic local SSE fixture: no AI inference, model usage, quality or billing claim. Whole compact JSON token estimates include all 211 schemas and every request replay. Incomplete default baseline is excluded from completed-task gains. SDK exchange estimates are not model-input measurements. Fresh processes have unflushed OS caches; both status reads query live session observations. Timings include the eight-second target lifetime and cleanup; single pairs establish no general speedup. Payload-exit cgroup counters are verified; final whole-scope counters may be unavailable.",
|
||||
"tokenizer": {
|
||||
"package": "0.12.0",
|
||||
"encoding": "cl100k_base"
|
||||
},
|
||||
"runtime_pins": {
|
||||
"baseline": {
|
||||
"source_base": "17f581ff75d2b6cf643a997275e3e296f6c95760",
|
||||
"patch_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"archive_sha256": "cd16ca3e36633459bffc10bb90d1fe4ff5887fdd403729765b9a812969013807"
|
||||
},
|
||||
"candidate": {
|
||||
"source_base": "32a4690059907f09fb29e284ad492d94c1722278",
|
||||
"patch_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"archive_sha256": "4ac402f1de93f76f71b7ccdb8b8e36d8c8d0a87b1184812f06393bfc6fadccba"
|
||||
}
|
||||
},
|
||||
"native": {
|
||||
"host": "Linux-6.1.0-53-cloud-amd64-x86_64-with-glibc2.36",
|
||||
"toolchain": {
|
||||
"node": "24.18.0",
|
||||
"npm": "11.16.0"
|
||||
},
|
||||
"frida_binding_package_version": "17.23.2",
|
||||
"files": [
|
||||
{
|
||||
"path": "/usr/bin/sleep",
|
||||
"bytes": 43888,
|
||||
"sha256": "4add4bb89d8ca0e3b1bd861130ddd7ae0fd9617a8055de0a38c8d2ca1ac95723"
|
||||
},
|
||||
{
|
||||
"path": "/usr/lib/x86_64-linux-gnu/libc.so.6",
|
||||
"bytes": 1926232,
|
||||
"sha256": "6b4a45352fd0c540a9c7c718f35ce8c8e46a4e482f9d3885a910c32d1a0e1421"
|
||||
},
|
||||
{
|
||||
"path": "/home/yann/yann/rea-perf-next-audit-20261020/node_modules/frida/package.json",
|
||||
"bytes": 2252,
|
||||
"sha256": "7efb59d2050b7b6304e1617f463c4eb7b2f7cb7e3e80e677976825a76ee31e8d"
|
||||
},
|
||||
{
|
||||
"path": "/home/yann/yann/rea-perf-next-audit-20261020/node_modules/frida/build/frida_binding.node",
|
||||
"bytes": 112775824,
|
||||
"sha256": "5b2db5ae57c058ad0765e627d9d9c9c98e4e90c09746c6f727b7a80769eed7a5"
|
||||
}
|
||||
],
|
||||
"qualifications": {
|
||||
"Inspector": "real REA MCP target, concurrent native metadata only oracle, 776 rows matched hash/length; not global completeness or execution proof",
|
||||
"Frida": "local native binding and controlled sleep spawn; caller script digest and procfs PID/birthtime/cgroup/libc path verified; native export address/global completeness oracle unavailable",
|
||||
"ADB": "default executable unavailable in prior public call; no real device qualified"
|
||||
}
|
||||
},
|
||||
"script_pins": {
|
||||
"frida_status_delivery.py": "ed19bcd003165c7b522c8cdd4799bcf987178e4b79fc8d5c2442012f3bf60b8d",
|
||||
"frida-status-costs.py": "2f3196bcc3198a265f7f4842effc8dce31669723b42f07367f065afee0073906",
|
||||
"frida-status-queries.mjs": "c5919ae8bba046909858e92a945b4472645bbacbfcf88dd75c89ba959a986980",
|
||||
"frida-status-wire.py": "b4d20e64bf77b7815bd43b52569b3e4756983b254ab44c50ddae874d1964ef69"
|
||||
},
|
||||
"wire": [
|
||||
{
|
||||
"case": "wire-v2-baseline-0",
|
||||
"task_completed": false,
|
||||
"mode": "complete",
|
||||
"budget": "client default",
|
||||
"requests": 8,
|
||||
"compactions": 0,
|
||||
"whole_json_tokens_proxy": 540065,
|
||||
"request_details": [
|
||||
{
|
||||
"whole_json_tokens_proxy": 62729,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 62917,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 63599,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 63739,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 68261,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 72785,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 72926,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 73109,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
}
|
||||
],
|
||||
"wall_seconds": 15.993,
|
||||
"cpu_seconds": 8.211179,
|
||||
"family_peak_mib": 830.6484375,
|
||||
"payload_cleanup_verified": true,
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"case": "wire-v2-baseline-300000",
|
||||
"task_completed": true,
|
||||
"mode": "complete",
|
||||
"budget": 300000,
|
||||
"requests": 8,
|
||||
"compactions": 0,
|
||||
"whole_json_tokens_proxy": 1049997,
|
||||
"request_details": [
|
||||
{
|
||||
"whole_json_tokens_proxy": 62715,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 62903,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 63577,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 63714,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 141108,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 218504,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 218646,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 218830,
|
||||
"rea_namespace_tokens_proxy": 53518,
|
||||
"rea_functions": 211
|
||||
}
|
||||
],
|
||||
"wall_seconds": 15.418,
|
||||
"cpu_seconds": 8.026944,
|
||||
"family_peak_mib": 824.28125,
|
||||
"payload_cleanup_verified": true,
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"case": "wire-v2-candidate-0",
|
||||
"task_completed": true,
|
||||
"mode": "summary",
|
||||
"budget": "client default",
|
||||
"requests": 8,
|
||||
"compactions": 0,
|
||||
"whole_json_tokens_proxy": 515539,
|
||||
"request_details": [
|
||||
{
|
||||
"whole_json_tokens_proxy": 62791,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 62978,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 63661,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 63803,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 64749,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 65697,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 65837,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 66023,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
}
|
||||
],
|
||||
"wall_seconds": 15.889,
|
||||
"cpu_seconds": 7.939387,
|
||||
"family_peak_mib": 823.69921875,
|
||||
"payload_cleanup_verified": true,
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"case": "wire-v2-candidate-300000",
|
||||
"task_completed": true,
|
||||
"mode": "summary",
|
||||
"budget": 300000,
|
||||
"requests": 8,
|
||||
"compactions": 0,
|
||||
"whole_json_tokens_proxy": 515713,
|
||||
"request_details": [
|
||||
{
|
||||
"whole_json_tokens_proxy": 62804,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 62990,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 63677,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 63815,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 64772,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 65731,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 65870,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
},
|
||||
{
|
||||
"whole_json_tokens_proxy": 66054,
|
||||
"rea_namespace_tokens_proxy": 53581,
|
||||
"rea_functions": 211
|
||||
}
|
||||
],
|
||||
"wall_seconds": 15.439,
|
||||
"cpu_seconds": 8.371664,
|
||||
"family_peak_mib": 813.6328125,
|
||||
"payload_cleanup_verified": true,
|
||||
"final_whole_scope_counters_verified": false
|
||||
}
|
||||
],
|
||||
"sdk": [
|
||||
{
|
||||
"case": "sdk-baseline-libc",
|
||||
"detail": "complete",
|
||||
"capture": "libc",
|
||||
"tool_calls": 8,
|
||||
"exported_bundle_bytes": 214214,
|
||||
"answer": {
|
||||
"pid": 517427,
|
||||
"state": "running",
|
||||
"scripts": 1,
|
||||
"messages": 50,
|
||||
"messages_truncated": false,
|
||||
"exports": 2963,
|
||||
"module_path": "/usr/lib/x86_64-linux-gnu/libc.so.6"
|
||||
},
|
||||
"status_calls": [
|
||||
{
|
||||
"name": "frida_session_status",
|
||||
"input": {
|
||||
"session_id": "ef8982d3-dbf2-43cb-8256-3a4f4bc83cc3"
|
||||
},
|
||||
"phase": "frida_session_status",
|
||||
"elapsed_ms": 197.58100300000024,
|
||||
"reply_bytes": 462831,
|
||||
"is_error": false
|
||||
},
|
||||
{
|
||||
"name": "frida_session_status",
|
||||
"input": {
|
||||
"session_id": "ef8982d3-dbf2-43cb-8256-3a4f4bc83cc3"
|
||||
},
|
||||
"phase": "frida_session_status",
|
||||
"elapsed_ms": 108.50572199999988,
|
||||
"reply_bytes": 462831,
|
||||
"is_error": false
|
||||
}
|
||||
],
|
||||
"one_catalog_plus_all_sdk_calls_tokens_proxy": 1186065,
|
||||
"wall_seconds": 13.759,
|
||||
"cpu_seconds": 6.097489,
|
||||
"family_peak_mib": 578.5625,
|
||||
"payload_cleanup_verified": true,
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"case": "sdk-baseline-small",
|
||||
"detail": "complete",
|
||||
"capture": "small",
|
||||
"tool_calls": 8,
|
||||
"exported_bundle_bytes": 2335,
|
||||
"answer": {
|
||||
"pid": 516970,
|
||||
"state": "running",
|
||||
"scripts": 1,
|
||||
"messages": 2,
|
||||
"messages_truncated": false,
|
||||
"exports": 0,
|
||||
"module_path": null
|
||||
},
|
||||
"status_calls": [
|
||||
{
|
||||
"name": "frida_session_status",
|
||||
"input": {
|
||||
"session_id": "3f1f778f-ca18-48ba-a7db-b58afbf4710d"
|
||||
},
|
||||
"phase": "frida_session_status",
|
||||
"elapsed_ms": 79.25232200000028,
|
||||
"reply_bytes": 2643,
|
||||
"is_error": false
|
||||
},
|
||||
{
|
||||
"name": "frida_session_status",
|
||||
"input": {
|
||||
"session_id": "3f1f778f-ca18-48ba-a7db-b58afbf4710d"
|
||||
},
|
||||
"phase": "frida_session_status",
|
||||
"elapsed_ms": 6.851230000000214,
|
||||
"reply_bytes": 2643,
|
||||
"is_error": false
|
||||
}
|
||||
],
|
||||
"one_catalog_plus_all_sdk_calls_tokens_proxy": 882323,
|
||||
"wall_seconds": 13.039,
|
||||
"cpu_seconds": 4.953337,
|
||||
"family_peak_mib": 577.140625,
|
||||
"payload_cleanup_verified": true,
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"case": "sdk-candidate-libc",
|
||||
"detail": "summary",
|
||||
"capture": "libc",
|
||||
"tool_calls": 8,
|
||||
"exported_bundle_bytes": 216931,
|
||||
"answer": {
|
||||
"pid": 517901,
|
||||
"state": "running",
|
||||
"scripts": 1,
|
||||
"messages": 50,
|
||||
"messages_truncated": false,
|
||||
"exports": 2963,
|
||||
"module_path": "/usr/lib/x86_64-linux-gnu/libc.so.6"
|
||||
},
|
||||
"status_calls": [
|
||||
{
|
||||
"name": "frida_session_status",
|
||||
"input": {
|
||||
"session_id": "9af7f188-05f8-4559-b570-98556196090e",
|
||||
"detail": "summary"
|
||||
},
|
||||
"phase": "frida_session_status",
|
||||
"elapsed_ms": 159.16798299999937,
|
||||
"reply_bytes": 5597,
|
||||
"is_error": false
|
||||
},
|
||||
{
|
||||
"name": "frida_session_status",
|
||||
"input": {
|
||||
"session_id": "9af7f188-05f8-4559-b570-98556196090e",
|
||||
"detail": "summary"
|
||||
},
|
||||
"phase": "frida_session_status",
|
||||
"elapsed_ms": 61.96135099999992,
|
||||
"reply_bytes": 5597,
|
||||
"is_error": false
|
||||
}
|
||||
],
|
||||
"one_catalog_plus_all_sdk_calls_tokens_proxy": 885626,
|
||||
"wall_seconds": 12.843,
|
||||
"cpu_seconds": 5.012703,
|
||||
"family_peak_mib": 579.0,
|
||||
"payload_cleanup_verified": true,
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"case": "sdk-candidate-small",
|
||||
"detail": "summary",
|
||||
"capture": "small",
|
||||
"tool_calls": 8,
|
||||
"exported_bundle_bytes": 5051,
|
||||
"answer": {
|
||||
"pid": 517162,
|
||||
"state": "running",
|
||||
"scripts": 1,
|
||||
"messages": 2,
|
||||
"messages_truncated": false,
|
||||
"exports": 0,
|
||||
"module_path": null
|
||||
},
|
||||
"status_calls": [
|
||||
{
|
||||
"name": "frida_session_status",
|
||||
"input": {
|
||||
"session_id": "1bcda0f5-e3cd-45ee-b82c-5886d8312bc6",
|
||||
"detail": "summary"
|
||||
},
|
||||
"phase": "frida_session_status",
|
||||
"elapsed_ms": 107.78244100000029,
|
||||
"reply_bytes": 5595,
|
||||
"is_error": false
|
||||
},
|
||||
{
|
||||
"name": "frida_session_status",
|
||||
"input": {
|
||||
"session_id": "1bcda0f5-e3cd-45ee-b82c-5886d8312bc6",
|
||||
"detail": "summary"
|
||||
},
|
||||
"phase": "frida_session_status",
|
||||
"elapsed_ms": 5.996030000000246,
|
||||
"reply_bytes": 5595,
|
||||
"is_error": false
|
||||
}
|
||||
],
|
||||
"one_catalog_plus_all_sdk_calls_tokens_proxy": 885676,
|
||||
"wall_seconds": 13.533,
|
||||
"cpu_seconds": 5.495048,
|
||||
"family_peak_mib": 576.48046875,
|
||||
"payload_cleanup_verified": true,
|
||||
"final_whole_scope_counters_verified": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
# Frida status delivery and runtime-observation audit
|
||||
|
||||
Issue: [#5640](https://github.com/morluto/rea/issues/5640). Paired runtime base:
|
||||
`17f581ff75d2b6cf643a997275e3e296f6c95760`. Final candidate source: `32a4690059907f09fb29e284ad492d94c1722278` (clean tree).
|
||||
Exact package, patch, native input,
|
||||
script and resource pins are in [the measurement record](frida-status-views.json).
|
||||
|
||||
The analyst requests a complete libc export inventory as retained evidence,
|
||||
then checks the session's identity/state, script identity, queued message count
|
||||
and truncation twice. The caller-authored script enumerates actual native module
|
||||
exports in a controlled `/usr/bin/sleep` spawn, in chunks of 64. Both workflows
|
||||
retain all captured messages and export the complete Evidence bundle.
|
||||
|
||||
Start, status and one-shot contracts share one canonical session envelope.
|
||||
Message JSON is validated at the Evidence owner; the summary validates the
|
||||
envelope without reinterpreting opaque payloads.
|
||||
|
||||
The owning application service now accepts optional `detail=summary` for
|
||||
`frida_session_status`. It reads the provider on every call, retains the original
|
||||
complete Evidence, then uses the existing shared analysis-view projector.
|
||||
The summary omits arbitrary message payloads and preserves all other status
|
||||
fields, the retained message count, truncation, exact parent identity/digest,
|
||||
provider and source parameters. Default and explicit complete delivery keep the
|
||||
original Evidence identity and payload. Historical CLI/file projections use the
|
||||
same projector. Source script decoding/representation Evidence and session
|
||||
resource ownership are unchanged.
|
||||
|
||||
## Completed tasks
|
||||
|
||||
The native Frida Node binding package 17.23.2 captured 2,963 libc exports in 50 queued
|
||||
messages. Independent checks establish the test-owned process birthtime, cgroup,
|
||||
PID and libc mapping; the selected script digest and contiguous chunks are
|
||||
checked against the complete retained parent. This does not independently
|
||||
validate every native export address or establish global event coverage.
|
||||
|
||||
| Public SDK task | Complete | Summary |
|
||||
| ------------------------------------------ | -------: | ------: |
|
||||
| Large-history status reply, bytes per call | 462,831 | 5,597 |
|
||||
| Small-history status reply, bytes per call | 2,643 | 5,595 |
|
||||
| Large complete bundle, bytes | 214,214 | 216,931 |
|
||||
| Small complete bundle, bytes | 2,335 | 5,051 |
|
||||
| Large task elapsed, seconds | 13.759 | 12.843 |
|
||||
| Large task CPU, seconds | 6.097 | 5.013 |
|
||||
| Large family peak, MiB | 578.6 | 579.0 |
|
||||
| Small task elapsed, seconds | 13.039 | 13.533 |
|
||||
| Small task CPU, seconds | 4.953 | 5.495 |
|
||||
| Small family peak, MiB | 577.1 | 576.5 |
|
||||
|
||||
Each SDK task discovers all 211 tools and executes eight public calls, including
|
||||
two status reads and complete export. Small history contains two readiness/
|
||||
completion messages. Fresh processes retain unflushed OS caches. The second
|
||||
status read is a repeated live observation, not a cache hit. Every task includes
|
||||
the controlled target's eight-second lifetime and verified natural exit.
|
||||
Single pairs and this fixed wait establish no general latency/CPU advantage.
|
||||
Large status calls took 159.2/62.0 ms versus 197.6/108.5 ms. The first small
|
||||
summary status was slower (107.8 versus 79.3 ms).
|
||||
|
||||
## Actual client context
|
||||
|
||||
Codex app-server 0.163.0 performs the native Frida workflow through the actual
|
||||
REA MCP package. A deterministic local Responses/SSE fixture selects the calls;
|
||||
there is no AI inference, paid model, model-quality or billing claim. The capture
|
||||
contains all 211 tool input schemas on each request and all history replays. `tiktoken`
|
||||
0.12.0 `cl100k_base` counts compact JSON as a token estimate, not actual model
|
||||
usage. Both completed paths use the same 300,000 tool-output budget.
|
||||
|
||||
| Completed client task | Complete | Summary |
|
||||
| -------------------------------------- | --------: | ------: |
|
||||
| Requests / compactions | 8 / 0 | 8 / 0 |
|
||||
| Whole request JSON token estimate, sum | 1,049,997 | 515,713 |
|
||||
| Elapsed seconds | 15.418 | 15.439 |
|
||||
| CPU seconds | 8.027 | 8.372 |
|
||||
| Family peak MiB | 824.3 | 813.6 |
|
||||
|
||||
The completed task uses **50.9% fewer estimated request tokens**, including
|
||||
schemas and continuations. Client elapsed and CPU time increased in this pair. At the client's
|
||||
default output budget, complete status JSON is truncated and the task is
|
||||
incomplete; summary status is intact and completes. That failed delivery is
|
||||
excluded from completed-task gains. Three acceptance tests exercise invalid
|
||||
JSON, incorrect counts/state/source linkage, missing chunks and wrong sessions.
|
||||
|
||||
The complete catalog plus all SDK exchanges also shrinks for the large case
|
||||
(1,186,065 to 885,626 estimated tokens), but grows for the small case (882,323 to
|
||||
885,676). These SDK exchange estimates include advertised output schemas and
|
||||
are **not** actual model-input measurements. Summary delivery is optional
|
||||
because its provenance wrapper costs more than a small complete observation.
|
||||
|
||||
## Resource accounting and limits
|
||||
|
||||
Runtime cases use one CPU, a 4 GiB cgroup ceiling, a 3 GiB early stop, no swap,
|
||||
192 tasks, and host-memory admission/early-stop guards. Payload exit, descendant
|
||||
cleanup and zero resource-limit/OOM events are verified while the scope observer
|
||||
remains alive. Final whole-scope counters disappear after teardown and remain
|
||||
unknown. Runtime family peaks stay below 1 GiB. Build/typecheck use separately
|
||||
guarded larger limits; they are not runtime performance measurements.
|
||||
|
||||
Full queued messages still undergo validation, Evidence creation and retention;
|
||||
this change primarily reduces delivery and repeated context. The extra derived
|
||||
record increases retained bundle size. Long-lived heap retention, many-script
|
||||
sessions, remote devices, binary messages and saturated capture budgets are not
|
||||
performance-qualified by these native cases. Boundary tests preserve malformed
|
||||
input, truncation, cancellation and cleanup semantics; simulated providers do
|
||||
not qualify real remote behavior.
|
||||
|
||||
## Other providers in this audit
|
||||
|
||||
Inspector was measured against a test-owned real REA MCP process while it
|
||||
handled a verified GitHub CLI Go build-information request. A concurrent native
|
||||
CDP metadata subscription matched all 776 retained scripts by hash and reported
|
||||
length: 565 file locations and 211 built-ins. The reply was 608,719 bytes;
|
||||
capture retained 996/996 events with no reported drop/truncation. The qualification
|
||||
run took 20.956 seconds, 18.076 CPU seconds and 987.4 MiB peak family memory,
|
||||
including the independent oracle. This is not a paired performance cohort,
|
||||
global completeness proof, source-byte validation or evidence of script
|
||||
execution. Only `Runtime.enable` and `Debugger.enable` were sent. A prior
|
||||
post-capture oracle missed 25 historical rows and was rejected; it does not
|
||||
establish an Inspector regression. Existing raw-null delivery, grouped location
|
||||
validation and eight-worker projection were already present and were preserved.
|
||||
|
||||
ADB's configured default executable is unavailable on this host, and no real
|
||||
device is qualified. This audit makes no ADB device-performance claim. Inspector
|
||||
selection/caching and Frida queue/filter redesign remain unimplemented without
|
||||
an equivalent completed-task measurement that justifies them.
|
||||
|
||||
The existing bundled Frida verifier also returned functional success, but its
|
||||
1,500 ms fixture outlived the verifier's 500 ms observation. Its payload-exit
|
||||
cleanup receipt was therefore unverified and is excluded from qualified
|
||||
resource/performance results. That unchanged verifier has no process birthtime
|
||||
stamp to safely wait on an arbitrary later PID. The paired harness instead owns
|
||||
the paused spawn and records birthtime/cgroup before resume, then waits for the
|
||||
original target's natural exit. No PID-only cleanup or target-kill policy was
|
||||
introduced.
|
||||
|
||||
The shared-service import also exposed an existing module-mock load-order
|
||||
assumption in the Evidence-file cancellation test. That one mocked test now
|
||||
runs in the existing isolated `process-global` lane, with an assertion that the
|
||||
mocked read actually ran. Other file/service tests keep the shared module graph.
|
||||
The final exact ten-file gate passes 225 tests with file parallelism disabled
|
||||
under one CPU. An earlier parallel gate hit one existing 30-second MCP lifecycle
|
||||
timeout; its serial retry passed without increasing a timeout or changing the
|
||||
production lifecycle contract.
|
||||
|
||||
## Reproduction
|
||||
|
||||
Use the pinned Node/npm toolchain and native Frida dependency. Freeze separate
|
||||
baseline/candidate built packages and install the same locked dependencies.
|
||||
Run each case independently with the resource observer:
|
||||
|
||||
```sh
|
||||
python3 experiments/analysis-efficiency/resource-run.py --observe-payload-exit -- \
|
||||
node experiments/analysis-efficiency/frida-status-queries.mjs \
|
||||
--runtime /absolute/built-package --output /absolute/new-case \
|
||||
--capture libc --detail summary
|
||||
```
|
||||
|
||||
Repeat with `--detail complete` and `--capture small`. For actual-client captures,
|
||||
`frida-status-wire.py --help` names the runtime, pinned Codex and Node executables,
|
||||
output directory and selected script file (`selected-script.js` from the SDK
|
||||
case). Use both `--mode complete`/`summary` and `--budget 300000`/`0`.
|
||||
`frida-status-costs.py` checks task/resource receipts and counts complete requests.
|
||||
The first wire attempt used ambiguous suffix matching for load/unload names;
|
||||
the fixture now selects exact operation names. Failed attempts remain outside
|
||||
the performance cohort. No production behavior changed to accommodate them.
|
||||
@@ -0,0 +1,299 @@
|
||||
"""Actual Codex wire capture with a deterministic local SSE backend; no AI inference."""
|
||||
import argparse
|
||||
import http.server
|
||||
import threading
|
||||
import asyncio
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import time
|
||||
import traceback
|
||||
import subprocess
|
||||
from frida_status_delivery import verify_frida_delivery
|
||||
|
||||
|
||||
|
||||
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")
|
||||
parser.add_argument("--script", type=Path, required=True)
|
||||
parser.add_argument("--expected-tools", type=int, default=211)
|
||||
parser.add_argument("--mode", choices=["complete", "summary"], required=True)
|
||||
parser.add_argument("--budget", type=int, default=30000)
|
||||
args = parser.parse_args()
|
||||
source=args.script.read_text()
|
||||
assert args.runtime.is_absolute() and args.output.is_absolute() and args.script.is_absolute()
|
||||
def delivered(pieces):
|
||||
assert isinstance(pieces,str) and pieces.startswith("Wall time: "), pieces[:300]
|
||||
_,sep,payload=pieces.partition("Output:\n");assert sep
|
||||
try: value=json.loads(payload)
|
||||
except json.JSONDecodeError: return {"_invalid_json_payload":payload}
|
||||
return value["result"] if "result" in value and "normalized_result" not in value else value
|
||||
def outputs_of(body):
|
||||
return {item["call_id"]:delivered(item["output"]) for item in body.get("input",[]) if item.get("type")=="function_call_output"}
|
||||
steps=7
|
||||
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)
|
||||
captured = []
|
||||
known_outputs = {}
|
||||
state = {"normal_requests":0,"compactions":0}
|
||||
class Capture(http.server.BaseHTTPRequestHandler):
|
||||
def log_message(self, *_):
|
||||
pass
|
||||
def do_POST(self):
|
||||
size = int(self.headers["Content-Length"])
|
||||
assert 0 < size <= 16 * 1024 * 1024
|
||||
assert self.headers.get("Content-Encoding", "identity") == "identity"
|
||||
body = json.loads(self.rfile.read(size))
|
||||
captured.append({"path": self.path, "body": body})
|
||||
(args.output / "model-requests.json").write_text(json.dumps(captured) + "\n")
|
||||
http_index = len(captured)
|
||||
known_outputs.update(outputs_of(body))
|
||||
names = [t.get("name", "") for ns in body.get("tools", []) if ns.get("type") == "namespace" and ns.get("name") == "mcp__rea_audit" for t in ns.get("tools", [])]
|
||||
if not names:
|
||||
last=body.get('input',[])[-1]
|
||||
assert 'CONTEXT CHECKPOINT COMPACTION' in json.dumps(last)
|
||||
state['compactions']+=1;assert state['compactions']<=1
|
||||
checkpoint={'fixture':'deterministic observed-ID checkpoint, no AI inference','task':'Check Frida session state, queued message count and truncation twice; retain/export complete observations and close instrumentation.','session_id':known_outputs['call_local_1']['session_id'],'delivered_call_ids':list(known_outputs),'remaining':'Continue scripted tools; source message bodies remain in retained Evidence.'}
|
||||
text=json.dumps(checkpoint)
|
||||
item={"type":"message","id":f"msg_checkpoint_{state['compactions']}","role":"assistant","status":"completed","content":[{"type":"output_text","text":text,"annotations":[]}]}
|
||||
captured[-1]['kind']='deterministic-compaction'
|
||||
else:
|
||||
state['normal_requests']+=1;index=state['normal_requests'];assert index<=steps+1
|
||||
captured[-1]['kind']='tool-planning-or-final'
|
||||
if index<=steps:
|
||||
if index==1:
|
||||
operation='start_frida_session';arguments={'mode':'spawn','device_id':'local','program':'/usr/bin/sleep','argv':['8']}
|
||||
else:
|
||||
session=known_outputs['call_local_1']['session_id']
|
||||
if index==2:
|
||||
operation='load_frida_script';arguments={'session_id':session,'source_kind':'inline','source':source}
|
||||
elif index==3:
|
||||
operation='resume_frida_session';arguments={'session_id':session}
|
||||
elif index in [4,5]:
|
||||
if index==4: time.sleep(0.75)
|
||||
operation='frida_session_status';arguments={'session_id':session,**({'detail':'summary'} if args.mode=='summary' else {})}
|
||||
elif index==6:
|
||||
operation='close_frida_session';arguments={'session_id':session}
|
||||
else:
|
||||
operation='export_evidence_bundle';arguments={'path':str(args.output/'evidence.json')}
|
||||
selected=[name for name in names if name == operation];assert len(selected)==1
|
||||
item={"type":"function_call","id":f"fc_local_{index}","call_id":f"call_local_{index}","name":selected[0],"namespace":"mcp__rea_audit","arguments":json.dumps(arguments),"status":"completed"}
|
||||
else:
|
||||
item={"type":"message","id":"msg_local_done","role":"assistant","status":"completed","content":[{"type":"output_text","text":"Scripted local Frida state and capture-coverage task complete.","annotations":[]}]}
|
||||
response_id=f"resp_local_{http_index}"
|
||||
base = {"id": response_id, "object": "response", "model": body.get("model"), "status": "completed", "output": [item]}
|
||||
events = [
|
||||
{"type": "response.created", "response": {**base, "status": "in_progress", "output": []}},
|
||||
{"type": "response.output_item.added", "output_index": 0, "item": {**item, "status": "in_progress"}},
|
||||
{"type": "response.output_item.done", "output_index": 0, "item": item},
|
||||
{"type": "response.completed", "response": base},
|
||||
]
|
||||
wire = "".join("event: " + e["type"] + "\ndata: " + json.dumps(e) + "\n\n" for e in events).encode()
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "text/event-stream")
|
||||
self.send_header("Content-Length", str(len(wire)))
|
||||
self.end_headers()
|
||||
self.wfile.write(wire)
|
||||
endpoint = http.server.HTTPServer(("127.0.0.1", 0), Capture)
|
||||
endpoint_thread = threading.Thread(target=endpoint.serve_forever, daemon=True)
|
||||
endpoint_thread.start()
|
||||
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 = 'model_provider = "rea_capture"\nmodel = "rea-wire-probe"\n[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())
|
||||
for name in ['start_frida_session','load_frida_script','resume_frida_session','frida_session_status','close_frida_session','export_evidence_bundle']:
|
||||
config += f'[mcp_servers.rea_audit.tools.{name}]\napproval_mode = "approve"\n'
|
||||
if args.budget: config += f'output_token_limit = {args.budget}\n'
|
||||
config += f'\n[model_providers.rea_capture]\nname = "Local scripted capture"\nbase_url = "http://127.0.0.1:{endpoint.server_port}/v1"\nwire_api = "responses"\nrequires_openai_auth = false\nsupports_websockets = false\nrequest_max_retries = 0\nstream_max_retries = 0\n'
|
||||
(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": "Retain a caller-requested native libc export inventory, check session identity/state, queued message count and truncation twice, close instrumentation and export complete Evidence.",
|
||||
"client": "Codex app-server 0.163.0", "model_turns_started": 1, "backend": "local deterministic SSE fixture; no AI model or paid service",
|
||||
"actual_model_usage": None, "input_profile": args.input_profile,
|
||||
"output_profile": args.output_profile, "calls": [], "observations": [],
|
||||
"unexpected_server_requests": [], "cleanup_failures": []}
|
||||
client_version = subprocess.check_output([str(args.codex), "--version"], env=environment, cwd=workspace, text=True, timeout=10).strip()
|
||||
assert client_version == "codex-cli 0.163.0", client_version
|
||||
transcript = []
|
||||
pending = {}
|
||||
completed_turn = asyncio.Event()
|
||||
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 value.get("method") == "turn/completed":
|
||||
report["turn"] = value["params"]["turn"]
|
||||
completed_turn.set()
|
||||
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 scripted metadata 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", "turn/start"]
|
||||
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)) == args.expected_tools
|
||||
assert "frida_session_status" in names
|
||||
output_schemas = sum(tool.get("outputSchema") is not None for tool in tools)
|
||||
assert output_schemas == (args.expected_tools 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")}
|
||||
await request("turn/start", {"threadId":thread_id,"input":[{"type":"text","text":"Retain a complete libc export inventory using the selected caller-authored script. Check session state and queued message capture count/truncation twice, close Frida instrumentation and export complete Evidence. The local fixture scripts the tools; no AI inference."}]})
|
||||
await asyncio.wait_for(completed_turn.wait(), 90)
|
||||
assert report["turn"]["status"] == "completed", report["turn"]
|
||||
assert state["normal_requests"]==steps+1
|
||||
assert len(captured)==steps+1+state["compactions"]
|
||||
outputs=known_outputs
|
||||
assert len(outputs)==steps
|
||||
bundle=json.loads((args.output/'evidence.json').read_text())
|
||||
delivery_complete=True
|
||||
answers=[]
|
||||
for index in [4,5]:
|
||||
delivered_result=outputs[f'call_local_{index}']
|
||||
answer=verify_frida_delivery(delivered_result,bundle,args.mode,args.budget,outputs['call_local_1'])
|
||||
if answer is None: delivery_complete=False
|
||||
else: answers.append(answer)
|
||||
loaded=next(e for e in bundle['records'] if e['operation']=='load_frida_script')
|
||||
assert loaded['normalized_result']['source_sha256']==hashlib.sha256(source.encode()).hexdigest()
|
||||
report['task_completed']=delivery_complete
|
||||
report['observations']={'answers':answers,'exported':True,'exported_bundle_bytes':(args.output/'evidence.json').stat().st_size,'tool_output_budget':args.budget or 'client default','source_sha256':hashlib.sha256(source.encode()).hexdigest()}
|
||||
report["captured_requests"]=len(captured)
|
||||
report["compaction_requests"]=state["compactions"]
|
||||
report["compaction_backend"]="Deterministic checkpoint of observed identifiers and retained-body location; no AI inference or quality claim."
|
||||
report["mode"]=args.mode
|
||||
report["request_tool_counts"] = [len(c["body"].get("tools", [])) for c in captured]
|
||||
assert not report["unexpected_server_requests"]
|
||||
report["status"] = "passed" if report["task_completed"] else "expected-incomplete-task"
|
||||
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")
|
||||
endpoint.shutdown()
|
||||
endpoint.server_close()
|
||||
endpoint_thread.join(5)
|
||||
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"] in ["passed","expected-incomplete-task"] and not report["cleanup_failures"] else 1
|
||||
|
||||
|
||||
raise SystemExit(asyncio.run(main()))
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Accept only complete client delivery of the selected Frida status question."""
|
||||
|
||||
|
||||
def verify_frida_delivery(delivered, bundle, mode, budget, session):
|
||||
if '_invalid_json_payload' in delivered:
|
||||
assert mode == 'complete' and budget == 0, 'A truncated summary never completes the task'
|
||||
return None
|
||||
value = delivered['normalized_result']
|
||||
parent = next(e for e in bundle['records'] if e['evidence_id'] == value['parent_evidence_id']) if mode == 'summary' else delivered
|
||||
assert parent in bundle['records'] and parent['operation'] == 'frida_session_status'
|
||||
assert parent['authority'] == 'external-service' and parent['environment'] is None
|
||||
assert parent['raw_result'] is None and parent['subject'] is None
|
||||
assert parent['parameters'] == {'session_id': session['session_id']}
|
||||
facts = parent['normalized_result']
|
||||
assert facts['state'] == 'running' and not facts['messages_truncated']
|
||||
assert facts['session_id'] == session['session_id'] and facts['pid'] == session['pid']
|
||||
payloads = [m['payload'] for m in facts['messages'] if m.get('type') == 'send']
|
||||
header = next(p for p in payloads if p['kind'] == 'module')
|
||||
complete = next(p for p in payloads if p['kind'] == 'complete')
|
||||
count = 0
|
||||
for chunk in [p for p in payloads if p['kind'] == 'exports']:
|
||||
assert chunk['offset'] == count
|
||||
count += len(chunk['items'])
|
||||
assert count == header['total'] == complete['total'] and count > 1000
|
||||
assert facts['pid'] == header['pid']
|
||||
if mode == 'summary':
|
||||
assert delivered in bundle['records']
|
||||
assert delivered['subject'] is None and delivered['raw_result'] is None
|
||||
assert delivered['authority'] == parent['authority']
|
||||
assert value['parent_operation'] == parent['operation']
|
||||
assert value['source_parameters'] == parent['parameters'] and value['source_provider'] == parent['provider']
|
||||
assert value['parent_digest'] == parent['evidence_id'][3:] and value['artifact'] is None
|
||||
assert value['summary'] == {**{k: v for k, v in facts.items() if k != 'messages'}, 'messages_retained': len(facts['messages'])}
|
||||
assert value['unknowns'] and value['limitations']
|
||||
return {'state': facts['state'], 'messages_retained': len(facts['messages']),
|
||||
'messages_truncated': facts['messages_truncated'], 'exports': count,
|
||||
'parent_evidence_id': parent['evidence_id']}
|
||||
@@ -0,0 +1,74 @@
|
||||
"""Count verified Go tasks; tokenizer estimates are not model usage or billing."""
|
||||
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 {}
|
||||
events = observed.get("resource_events", {})
|
||||
memory = events.get("memory.events", {})
|
||||
verified = (report["status"] == "observed"
|
||||
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 observed.get("payload_cleanup_verified") is True
|
||||
and observed.get("payload_exit_code") == 0
|
||||
and all(memory.get(key) == 0 for key in ["high", "max", "oom", "oom_kill"])
|
||||
and events.get("pids.events", {}).get("max") == 0)
|
||||
count = lambda value: len(encoding.encode(value, disallowed_special=()))
|
||||
inputs = count(compact([{key: tool[key] for key in ["name", "description", "inputSchema"]}
|
||||
for tool in catalog["tools"]]))
|
||||
arguments = sum(count(compact({"name": event["name"], "arguments": event["input"]}))
|
||||
for event in transcript)
|
||||
replies = sum(count(block["text"]) for event in transcript
|
||||
for block in event["reply"]["content"] if block["type"] == "text")
|
||||
cpu = observed.get("cpu_stat", {}).get("usage_usec")
|
||||
return {"case": path.name, "completed_with_verified_cleanup": verified,
|
||||
"mode": report["mode"], "warm_diagnostic": report["warm_diagnostic"],
|
||||
"answer": report["answer"], "parent_evidence_id": report["parent_evidence_id"],
|
||||
"artifact_sha256": report["artifact_sha256"],
|
||||
"complete_result_sha256": report["complete_result_file_sha256"],
|
||||
"calls": report["calls"], "wall_seconds": scope["elapsed_seconds"],
|
||||
"cpu_seconds": None if cpu is None else cpu / 1e6,
|
||||
"kernel_family_peak_bytes": observed.get("memory_peak_bytes"),
|
||||
"events_at_payload_exit": events,
|
||||
"final_whole_scope_events": scope.get("resource_events_final"),
|
||||
"token_proxy": {"input_catalog": inputs, "arguments": arguments,
|
||||
"text_replies": replies, "combined": inputs + arguments + replies,
|
||||
"complete_transport_catalog": count(compact(catalog))},
|
||||
"sha256": {file.name: hashlib.sha256(file.read_bytes()).hexdigest()
|
||||
for file 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]
|
||||
receipt = {"tiktoken": tiktoken.__version__, "encoding": encoding.name,
|
||||
"actual_model_usage": None,
|
||||
"projection": "All discovered input schemas once, plus every call's arguments and text replies, including export. Full transport catalog counted separately; no model turn or host injection measured.",
|
||||
"recordings": records}
|
||||
args.output.write_text(json.dumps(receipt, ensure_ascii=False, indent=2) + "\n")
|
||||
assert all(record["completed_with_verified_cleanup"] for record in records)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,81 @@
|
||||
"""Pin a completed Go report only after checking native build text and file bytes."""
|
||||
import argparse
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--evidence", type=Path, required=True)
|
||||
parser.add_argument("--input", type=Path, required=True)
|
||||
parser.add_argument("--go", type=Path, required=True)
|
||||
parser.add_argument("--dependency", required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
source = args.input.read_bytes()
|
||||
evidence = json.loads(args.evidence.read_text())
|
||||
if "records" in evidence:
|
||||
evidence = next(record for record in evidence["records"]
|
||||
if record["operation"] == "inspect_go_binary"
|
||||
and record["parameters"] == {"path": str(args.input)})
|
||||
report = evidence["normalized_result"]
|
||||
info = report["build_info"]
|
||||
digest = hashlib.sha256(source).hexdigest()
|
||||
assert report["artifact"] == {"path": str(args.input), "sha256": digest,
|
||||
"bytes": len(source)}
|
||||
native = subprocess.check_output(
|
||||
[str(args.go), "version", "-m", str(args.input)], text=True)
|
||||
lines = native.splitlines()
|
||||
assert info["go_version"] == lines[0].rsplit(" ", 1)[1]
|
||||
text = "\n".join(line.removeprefix("\t") for line in lines[1:]) + "\n"
|
||||
assert info["module_text"] == text
|
||||
for name in ["version", "module"]:
|
||||
location = info[name + "_location"]
|
||||
key = ("go_version" if name == "version" else "module") + "_bytes_base64"
|
||||
assert source[location["offset"]:location["offset"] + location["bytes"]] == base64.b64decode(info[key])
|
||||
# Module identity columns are literal tab-separated fields. Preserve empty
|
||||
# sums, development versions, original ordering and local replacements.
|
||||
dependencies = []
|
||||
main_module = None
|
||||
previous = None
|
||||
for line in text.splitlines():
|
||||
columns = line.split("\t")
|
||||
tag = columns[0]
|
||||
if tag not in ["dep", "mod", "=>"]:
|
||||
continue
|
||||
assert len(columns) in [3, 4], columns
|
||||
item = {"path": columns[1], "version": columns[2],
|
||||
"sum": columns[3] if len(columns) == 4 else None}
|
||||
if tag == "=>":
|
||||
assert previous is not None
|
||||
previous["replacement"] = item
|
||||
else:
|
||||
item["replacement"] = None
|
||||
previous = item
|
||||
if tag == "mod":
|
||||
main_module = item
|
||||
else:
|
||||
dependencies.append(item)
|
||||
assert info["module"]["main"] == main_module
|
||||
assert info["module"]["dependencies"] == dependencies
|
||||
answer = {"go_version": info["go_version"], "main": main_module,
|
||||
"runtime_dependencies": "unknown",
|
||||
"dependencies": [{"index": index, **item}
|
||||
for index, item in enumerate(dependencies)
|
||||
if item["path"] == args.dependency]}
|
||||
complete = args.output.with_suffix(".complete-result.json")
|
||||
complete.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n")
|
||||
receipt = {"artifact_sha256": digest, "dependency_path": args.dependency,
|
||||
"dependency_count": len(dependencies), "answer": answer,
|
||||
"complete_result_path": str(complete.resolve()),
|
||||
"native_text_sha256": hashlib.sha256(native.encode()).hexdigest(),
|
||||
"native_go_path": str(args.go),
|
||||
"native_go_sha256": hashlib.sha256(args.go.read_bytes()).hexdigest()}
|
||||
args.output.write_text(json.dumps(receipt, ensure_ascii=False, indent=2) + "\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,195 @@
|
||||
# Selected Go build metadata
|
||||
|
||||
Addresses [#5499](https://github.com/morluto/rea/issues/5499). A compiler/main-module
|
||||
identity question previously required the complete Go report, including raw framed
|
||||
bytes, base64, module text and every parsed dependency. Opt-in `detail: summary`
|
||||
retains the same complete parent and returns identity, source ranges, counts and
|
||||
explicit unknowns. Default complete delivery remains compatible.
|
||||
|
||||
`inspect_analysis_view` reuses that parent for `kind: go`, collection
|
||||
`dependencies` or `settings`. Exact paths/keys and original indices intersect;
|
||||
fields select columns, while every row keeps its original index. Pagination
|
||||
counts matching rows in producer order, including duplicate module paths. The
|
||||
view never reopens a target or reparses module text. Unknown lines and original
|
||||
bytes remain in the complete parent. CLI summaries save that parent in a new
|
||||
Evidence bundle before returning their projection.
|
||||
|
||||
Only immutable authenticated Evidence results reuse schema validation, keyed
|
||||
by their object identity for the Evidence lifetime. Mutable inline results are
|
||||
validated on each call. Subject identity is checked separately on every view.
|
||||
There is no live-file cache. Only page rows are projected; inherited messages
|
||||
use the existing bounded, deduplicating view-message owner.
|
||||
|
||||
## Question and independent checks
|
||||
|
||||
The main artifact is the official GitHub CLI v2.102.0 Linux amd64 release:
|
||||
42,086,560 bytes, SHA-256
|
||||
`7469124f706944133d6a169691dd1c6c3511b12e85878d255e044e2948df4c9b`.
|
||||
The archive digest matches its official GitHub asset metadata. Its embedded
|
||||
compiler is `go1.27.1`, main module `github.com/cli/cli/v2@v2.102.0`, and it has
|
||||
165 parsed dependency records. The selected `golang.org/x/crypto@v0.57.0` record
|
||||
keeps original index 152. These are embedded observations, not authentication
|
||||
of the compiler, supply chain or runtime behavior.
|
||||
|
||||
Native `go version -m` independently matches module text, main identity and all
|
||||
165 dependency identities in original order. Original file ranges match the
|
||||
reported compiler and framed module bytes. Before/after exports have identical
|
||||
complete normalized results and parent Evidence identity. The small real case
|
||||
is the installed 12,366,352-byte Go tool: embedded `go1.23.5`, no decoded main
|
||||
module and zero retained dependencies, with dependency coverage still unknown.
|
||||
REA reads both inputs without executing them. The installed Go tool also runs
|
||||
as the trusted native verifier; the downloaded GitHub CLI binary is never
|
||||
executed.
|
||||
|
||||
Timing baseline is main `ee12d517a25d34853d40e31fc039c7d1576c60e1`. Packaged runtimes
|
||||
and source patch are pinned in the [receipt](go-build-views.json); later changes
|
||||
do not relabel these timings. Node 24.18.0/npm 11.16.0, MCP SDK 2.3.1, Linux
|
||||
x86-64. Fresh processes use unflushed OS caches; the SDK controller pre-reads
|
||||
the input for verification. These are not disk-cold measurements.
|
||||
|
||||
## Actual client context, including every request
|
||||
|
||||
The [wire driver](go-build-wire.py) starts actual Codex app-server 0.163.0 with a
|
||||
fresh account/workspace and a loopback deterministic Responses SSE backend.
|
||||
It starts a scripted turn and real MCP calls, with **no AI inference or paid
|
||||
service**. All 210 tools remain discovered/advertised. Full input/output
|
||||
profiles are explicit, and each relevant tool has a 30,000-token output budget
|
||||
so complete JSON is delivered without retries or truncation. This does not
|
||||
qualify another client's defaults or a real model's reasoning/schema acceptance.
|
||||
|
||||
All original facts and parent identities match the native oracle. Export cases
|
||||
also verify the complete parent. The [counter](go-build-wire-costs.py) counts
|
||||
**every captured request**, including repeated schemas and accumulated tool
|
||||
outputs, using `tiktoken 0.12.0` / `cl100k_base` on compact request JSON. These are
|
||||
serialization-token proxies; actual model rendering, usage and cache billing
|
||||
remain unmeasured.
|
||||
|
||||
| Completed question | Requests before → after | Whole request JSON proxy before → after |
|
||||
| ------------------------------------------------------ | ----------------------: | --------------------------------------: |
|
||||
| Compiler, main module and dependency count | 2 → 2 | 158,529 → 123,368 (22.2% less) |
|
||||
| Compiler, main module and one selected dependency | 2 → 3 | 158,538 → 186,812 (17.8% more) |
|
||||
| Same selected dependency question plus Evidence export | 3 → 4 | 256,342 → 250,352 (2.3% less) |
|
||||
|
||||
The actual REA namespace costs 51,821 → 51,983 proxy tokens **per request**.
|
||||
An extra round can outweigh a smaller reply. Summary is useful when identity
|
||||
and counts already answer the question; complete delivery remains useful for
|
||||
an initial compound question. Retained views provide selected follow-up facts,
|
||||
without a claim that every such composition saves total context.
|
||||
|
||||
One recording per cell: identity wall/CPU/family peak is
|
||||
6.970 s / 6.331 s / 861.5 MiB before and 7.069 s / 6.457 s / 857.0 MiB after.
|
||||
Compound answer-only is 6.973 / 6.333 / 865.0 before and 6.969 / 6.348 / 861.6
|
||||
after. Export is 7.175 / 6.537 / 866.7 before and 7.274 / 6.568 / 872.6 after.
|
||||
These scripted-client observations establish no universal latency or memory
|
||||
gain, and do not measure model inference time.
|
||||
|
||||
## Direct SDK task and cold/warm costs
|
||||
|
||||
The [SDK driver](go-build-views.mjs) discovers all tools, answers the compound
|
||||
question, verifies exact facts and exports complete Evidence. Before: one
|
||||
complete inspection and export. After: summary, selected dependency and export.
|
||||
For the zero-dependency small case, the summary avoids an unnecessary empty
|
||||
query. Two repetitions alternate version/target order. All 208 other named
|
||||
catalog definitions agree; only Go inspection and the retained-view tool change.
|
||||
|
||||
| Input | Whole wall s before → after | Family CPU s before → after | Kernel family peak MiB before → after |
|
||||
| ---------- | --------------------------- | --------------------------- | ------------------------------------- |
|
||||
| GitHub CLI | 4.855–4.952 → 6.370–6.564 | 4.682–4.725 → 6.175–6.400 | 671.2–680.1 → 846.4–868.7 |
|
||||
| Go tool | 4.349–4.351 → 4.250–4.450 | 4.093–4.140 → 4.092–4.179 | 548.4–548.6 → 544.3–555.6 |
|
||||
|
||||
Cold selected-query calls take 1.719–1.751 s in the SDK task, including its
|
||||
output-schema validation. A separate warm diagnostic takes 1.712 s for the first
|
||||
query and 32.86 ms for the repeat in one session; both the client validator and
|
||||
retained facts are warm. This is not a paired whole-task speedup. The complete
|
||||
warm diagnostic is separately recorded, not mixed into the eight matched runs.
|
||||
|
||||
The [SDK counter](go-build-view-costs.py) counts the discovered input catalog
|
||||
once plus all arguments and text replies, including export: 229,183 → 195,643
|
||||
for GitHub CLI and 194,854 → 194,724 for the small case. Catalog cost grows
|
||||
193,192 → 193,652. This catalog-once projection is **not** the actual host
|
||||
exchange above; omitting repeated schemas would hide the compound-task penalty.
|
||||
|
||||
All qualified recordings use one CPU, a 4 GiB hard/3 GiB early family memory
|
||||
boundary, no swap and 192 tasks. Verified payload exits are zero, only the
|
||||
observer remains, and observed memory/task resource events are zero. Kernel
|
||||
family peaks include attributed file cache. Final whole-scope counters are
|
||||
unavailable. Release acquisition, oracle preparation and compilation are outside
|
||||
the timed tasks. An initial symlink input was correctly refused; qualification
|
||||
explicitly selects its regular target. One development static check was stopped
|
||||
by the host-available-memory guard, with owned cleanup verified and no observed
|
||||
OOM; the unchanged guard succeeded after headroom recovered.
|
||||
|
||||
## Coverage and reproduction
|
||||
|
||||
Executed: 10 files / 202 tests covering existing Go readers, mappings, provider,
|
||||
CLI/MCP and Rust readers, plus selected rows, duplicate paths, empty filters,
|
||||
identity-only fields, malformed/mutable parents, source conflicts, bounded
|
||||
messages and unavailable metadata. Actual CLI complete output is identical
|
||||
before/after; summary bundles retain that exact parent, and file-based CLI
|
||||
selected queries match the native oracle. Full `npm run check` passed.
|
||||
After rebasing onto main `c374c2fe7`, the 202 focused tests, full
|
||||
`npm run check` and `npm run docs:check` (including five README consistency
|
||||
tests) pass again. A real SDK selected query and complete export also pass
|
||||
against that rebuilt runtime, retaining the original parent identity and all
|
||||
165 dependencies. These checks do not relabel the frozen timing cohort.
|
||||
|
||||
A review found that a transcript-write failure could skip the SDK driver's
|
||||
cleanup. The shared finalizer now closes owned resources before publishing the
|
||||
optional transcript and records publication failures separately. All 11 receipt
|
||||
failure tests pass. A real selected SDK task and full export pass again; the
|
||||
same task with an injected transcript-write failure exits 1, preserves its
|
||||
answer and failure receipt, and verifies that the process family was cleaned.
|
||||
These qualification runs do not replace the frozen performance measurements.
|
||||
|
||||
A second review found that a valid external parent could disagree with its
|
||||
report's architecture. Go domain now owns the existing architecture-to-subject
|
||||
mapping; both Evidence production and every Go view use it. The view checks
|
||||
mapped architecture as well as path, digest and format, including `null` for
|
||||
unrecognized report names. All 213 focused tests pass, including authenticated
|
||||
inline/file conflicts and known-alias/unknown-architecture cases.
|
||||
|
||||
Initial CI also exposed two existing baseline failures. On unmodified `c374c2fe7`,
|
||||
`src/cliJsonPaths.test.ts` has the same three URL-message assertion failures.
|
||||
The duplication gate is already above its 3.5% threshold: 31,737 / 899,602 lines
|
||||
(3.52789%) on baseline versus 31,748 / 900,474 (3.52570%) on the initial PR head.
|
||||
These were reproduced separately with the pinned toolchain, bounded resources
|
||||
and verified cleanup; this PR leaves those unrelated failures unchanged.
|
||||
|
||||
Final qualification rebased onto main `cf5194f68`, preserving its new IL2CPP
|
||||
view contract alongside the Go additions. All 258 tests in 12 files pass,
|
||||
including 32 IL2CPP view tests and the now-fixed CLI path assertions. Full
|
||||
`npm run check`, `npm run docs:check` plus five README tests, and the real SDK
|
||||
selected query/full export pass again. Complete parent identity and all 165
|
||||
dependencies remain exact. Duplication now passes: baseline 31,644 / 904,778
|
||||
lines (3.49743%), PR 31,612 / 905,740 (3.49018%), against 3.5%.
|
||||
One latest-main build was guard-stopped during a host-memory dip; its owned
|
||||
scope was verified inactive with zero OOM events, then the unchanged guard's
|
||||
retry passed. These results qualify the rebased source, not new task timings.
|
||||
|
||||
The broader metadata audit also read three real Rust binaries. Alpine ripgrep
|
||||
14.1.1-r0's original `.dep-v0` matches native `readelf` coordinates and independent
|
||||
zlib/JSON decoding: 42 packages, 88 indexed edges, root index 33. The other two
|
||||
have no dependency section, which remains unknown coverage. Compiler-path
|
||||
inferences in all three match native read-only ranges and original byte offsets;
|
||||
they remain inferences. These fast metadata reads did not justify a Rust cache.
|
||||
The Alpine archive is pinned by observed SHA-256; APK signatures were not checked.
|
||||
Real provider verification here covers Linux ELF; PE/Mach-O use authored fixtures.
|
||||
|
||||
To reproduce, use built baseline/candidate runtimes and the pinned public input.
|
||||
Create a baseline complete CLI Evidence JSON, then pin the independent oracle:
|
||||
|
||||
```sh
|
||||
python3 experiments/analysis-efficiency/go-build-view-oracle.py \
|
||||
--evidence /tmp/baseline-evidence.json --input /absolute/gh \
|
||||
--go /absolute/go --dependency golang.org/x/crypto --output /tmp/go-oracle.json
|
||||
```
|
||||
|
||||
Run `go-build-views.mjs` with absolute `--runtime`, `--input`, `--oracle` and
|
||||
new `--output` directories, and `--mode complete` or `selected`, under
|
||||
`resource-run.py --observe-payload-exit`. `--warm` adds a separate diagnostic.
|
||||
The wire driver additionally requires pinned `--codex` and `--node`, and takes
|
||||
`--question identity --mode complete|summary` or
|
||||
`--question dependency --mode complete|selected`; `--export` adds export.
|
||||
All wire recordings require the same resource guard for owned-scope cleanup.
|
||||
Counter scripts take `--output` and recording directories, with their guard
|
||||
logs at the same path plus `.log`. Both counters require `tiktoken==0.12.0`.
|
||||
@@ -0,0 +1,228 @@
|
||||
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 { createRequire } from "node:module";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { finished } from "node:stream/promises";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { parseArgs } from "node:util";
|
||||
import { createRetainedQueryCaller } from "./retained-query-calls.mjs";
|
||||
import { finalizeRetainedQueryReceipt } from "./retained-query-finalization.mjs";
|
||||
|
||||
const { values } = parseArgs({
|
||||
options: {
|
||||
runtime: { type: "string" },
|
||||
input: { type: "string" },
|
||||
oracle: { type: "string" },
|
||||
output: { type: "string" },
|
||||
mode: { type: "string" },
|
||||
warm: { type: "boolean", default: false },
|
||||
},
|
||||
});
|
||||
for (const key of ["runtime", "input", "oracle", "output"])
|
||||
assert.equal(values[key], resolve(values[key]));
|
||||
assert.ok(["complete", "selected"].includes(values.mode));
|
||||
const oracle = JSON.parse(await readFile(values.oracle, "utf8"));
|
||||
const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
||||
const bytes = await readFile(values.input);
|
||||
assert.equal(sha256(bytes), oracle.artifact_sha256);
|
||||
const require = createRequire(join(values.runtime, "package.json"));
|
||||
const { Client } = await import(
|
||||
pathToFileURL(require.resolve("@modelcontextprotocol/client"))
|
||||
);
|
||||
const { StdioClientTransport } = await import(
|
||||
pathToFileURL(require.resolve("@modelcontextprotocol/client/stdio"))
|
||||
);
|
||||
await mkdir(values.output, { mode: 0o700 });
|
||||
const account = await mkdtemp(join(tmpdir(), "rea-go-build-views-"));
|
||||
const report = {
|
||||
question:
|
||||
"Report compiler, main module and the selected embedded dependency; preserve complete original Evidence.",
|
||||
mode: values.mode,
|
||||
warm_diagnostic: values.warm,
|
||||
calls: [],
|
||||
artifact_sha256: oracle.artifact_sha256,
|
||||
};
|
||||
const client = new Client({ name: "rea-go-build-views", version: "1" });
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [join(values.runtime, "scripts/rea.mjs"), "mcp"],
|
||||
env: {
|
||||
HOME: account,
|
||||
PATH: process.env.PATH,
|
||||
NODE_OPTIONS: "--max-old-space-size=2048",
|
||||
UV_THREADPOOL_SIZE: "1",
|
||||
REA_LOG_LEVEL: "silent",
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
const serverLog = createWriteStream(join(values.output, "server.log"));
|
||||
const serverLogFinished = finished(serverLog);
|
||||
serverLogFinished.catch(() => {});
|
||||
transport.stderr?.pipe(serverLog);
|
||||
const transcript = [];
|
||||
const call = createRetainedQueryCaller(client, (receipt, event) => {
|
||||
report.calls.push(receipt);
|
||||
transcript.push(event);
|
||||
});
|
||||
async function result(name, input, phase = name) {
|
||||
const reply = await call(name, input, phase);
|
||||
assert.ok(!reply.isError, JSON.stringify(reply));
|
||||
assert.ok(reply.structuredContent);
|
||||
assert.equal(reply.content.length, 1);
|
||||
assert.equal(reply.content[0].type, "text");
|
||||
assert.deepEqual(JSON.parse(reply.content[0].text), reply.structuredContent);
|
||||
return reply.structuredContent;
|
||||
}
|
||||
try {
|
||||
await client.connect(transport);
|
||||
const catalog = await client.listTools();
|
||||
assert.equal(catalog.nextCursor, undefined);
|
||||
report.catalog_tools = catalog.tools.length;
|
||||
await writeFile(join(values.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: 60000,
|
||||
toolDefinition: definitions.get(input.name),
|
||||
});
|
||||
const inspected = await result(
|
||||
"inspect_go_binary",
|
||||
{
|
||||
path: values.input,
|
||||
...(values.mode === "selected" ? { detail: "summary" } : {}),
|
||||
},
|
||||
"inspection:first",
|
||||
);
|
||||
let answer;
|
||||
if (values.mode === "complete") {
|
||||
report.parent_evidence_id = inspected.evidence_id;
|
||||
const info = inspected.normalized_result.build_info;
|
||||
assert.ok(info);
|
||||
answer = {
|
||||
go_version: info.go_version,
|
||||
main: info.module.main,
|
||||
runtime_dependencies: "unknown",
|
||||
dependencies: info.module.dependencies.flatMap((row, index) =>
|
||||
row.path === oracle.dependency_path ? [{ index, ...row }] : [],
|
||||
),
|
||||
};
|
||||
} else {
|
||||
const summary = inspected.normalized_result;
|
||||
assert.equal(summary.kind, "summary");
|
||||
report.parent_evidence_id = summary.parent_evidence_id;
|
||||
assert.equal(summary.source_coverage.runtime_dependencies, "unknown");
|
||||
assert.equal(
|
||||
summary.summary.build_info.module.available_counts.dependencies,
|
||||
oracle.dependency_count,
|
||||
);
|
||||
const input = {
|
||||
source: {
|
||||
kind: "retained-evidence",
|
||||
evidence_id: report.parent_evidence_id,
|
||||
},
|
||||
view: {
|
||||
kind: "go",
|
||||
collection: "dependencies",
|
||||
paths: [oracle.dependency_path],
|
||||
fields: ["path", "version", "sum", "replacement"],
|
||||
offset: 0,
|
||||
limit: 64,
|
||||
},
|
||||
};
|
||||
let dependencies = [];
|
||||
if (summary.summary.build_info.module.available_counts.dependencies > 0) {
|
||||
const selected = await result(
|
||||
"inspect_analysis_view",
|
||||
input,
|
||||
"retained-query:first",
|
||||
);
|
||||
assert.equal(selected.normalized_result.coverage.exhausted, true);
|
||||
assert.equal(
|
||||
selected.normalized_result.source_coverage.runtime_dependencies,
|
||||
"unknown",
|
||||
);
|
||||
dependencies = selected.normalized_result.page.rows;
|
||||
if (values.warm) {
|
||||
const warm = await result(
|
||||
"inspect_analysis_view",
|
||||
input,
|
||||
"retained-query:warm-diagnostic",
|
||||
);
|
||||
assert.deepEqual(warm, selected);
|
||||
}
|
||||
}
|
||||
answer = {
|
||||
go_version: summary.summary.build_info.go_version,
|
||||
main: summary.summary.build_info.module.main,
|
||||
runtime_dependencies: "unknown",
|
||||
dependencies,
|
||||
};
|
||||
}
|
||||
assert.deepEqual(answer, oracle.answer);
|
||||
report.answer = answer;
|
||||
await result("export_evidence_bundle", {
|
||||
path: join(values.output, "evidence.json"),
|
||||
});
|
||||
const bundle = JSON.parse(
|
||||
await readFile(join(values.output, "evidence.json"), "utf8"),
|
||||
);
|
||||
const parent = bundle.records.find(
|
||||
(e) => e.evidence_id === report.parent_evidence_id,
|
||||
);
|
||||
assert.ok(parent);
|
||||
assert.equal(parent.operation, "inspect_go_binary");
|
||||
assert.deepEqual(parent.parameters, { path: values.input });
|
||||
assert.equal(
|
||||
parent.normalized_result.artifact.sha256,
|
||||
oracle.artifact_sha256,
|
||||
);
|
||||
// The pre-change normalized result was independently checked against native
|
||||
// go version -m and its file-backed bytes before pinning this exact JSON.
|
||||
const expected = JSON.parse(
|
||||
await readFile(oracle.complete_result_path, "utf8"),
|
||||
);
|
||||
assert.deepEqual(parent.normalized_result, expected);
|
||||
assert.equal(
|
||||
parent.normalized_result.build_info.module.dependencies.length,
|
||||
oracle.dependency_count,
|
||||
);
|
||||
for (const name of ["version", "module"]) {
|
||||
const info = parent.normalized_result.build_info;
|
||||
const location = info[name + "_location"];
|
||||
const encoded =
|
||||
info[(name === "version" ? "go_version" : "module") + "_bytes_base64"];
|
||||
assert.deepEqual(
|
||||
bytes.subarray(location.offset, location.offset + location.bytes),
|
||||
Buffer.from(encoded, "base64"),
|
||||
);
|
||||
}
|
||||
assert.equal(sha256(await readFile(values.input)), oracle.artifact_sha256);
|
||||
report.oracle_file_sha256 = sha256(await readFile(values.oracle));
|
||||
report.complete_result_file_sha256 = sha256(
|
||||
await readFile(oracle.complete_result_path),
|
||||
);
|
||||
report.status = "observed";
|
||||
} catch (cause) {
|
||||
report.status = "failed";
|
||||
report.failure = String(cause?.stack ?? cause);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
if (
|
||||
!(await finalizeRetainedQueryReceipt({
|
||||
report,
|
||||
client,
|
||||
transport,
|
||||
account,
|
||||
serverLog,
|
||||
serverLogFinished,
|
||||
output: values.output,
|
||||
transcript,
|
||||
}))
|
||||
)
|
||||
process.exitCode = 1;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
"""Count entire captured Codex request sequences, without claiming model usage."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import tiktoken
|
||||
|
||||
|
||||
def compact(value):
|
||||
return json.dumps(value, ensure_ascii=False, separators=(",", ":"))
|
||||
|
||||
|
||||
def request_text(body):
|
||||
pieces = []
|
||||
if isinstance(body.get("instructions"), str):
|
||||
pieces.append(body["instructions"])
|
||||
for item in body.get("input", []):
|
||||
kind = item.get("type")
|
||||
if kind == "function_call_output":
|
||||
pieces.append(item["output"])
|
||||
elif kind == "function_call":
|
||||
pieces.append(item["arguments"])
|
||||
elif kind == "message" or "role" in item:
|
||||
for part in item.get("content", []):
|
||||
if isinstance(part, dict) and isinstance(part.get("text"), str):
|
||||
pieces.append(part["text"])
|
||||
return pieces
|
||||
|
||||
|
||||
def inspect(root, encoding):
|
||||
report = json.loads((root / "report.json").read_text())
|
||||
assert (report["status"] == "passed" and not report["cleanup_failures"]
|
||||
and report["appserver_exit_code"] == 0)
|
||||
requests = json.loads((root / "model-requests.json").read_text())
|
||||
whole, namespaces, texts = [], [], []
|
||||
count = lambda value: len(encoding.encode(value, disallowed_special=()))
|
||||
for event in requests:
|
||||
body = event["body"]
|
||||
whole.append(count(compact(body)))
|
||||
namespace = next(tool for tool in body["tools"]
|
||||
if tool.get("type") == "namespace"
|
||||
and tool["name"] == "mcp__rea_audit")
|
||||
namespaces.append(count(compact(namespace)))
|
||||
texts.append(sum(count(piece) for piece in request_text(body)))
|
||||
log = root.with_suffix(".log")
|
||||
scope = next(json.loads(line) for line in reversed(log.read_text().splitlines())
|
||||
if line.startswith('{"resource_scope":'))
|
||||
observed = scope["payload_resource_observation"]
|
||||
assert (scope["abort"] is None and scope["payload_resource_observation_verified"]
|
||||
and observed["payload_cleanup_verified"] and observed["payload_exit_code"] == 0)
|
||||
memory = observed["resource_events"]["memory.events"]
|
||||
assert all(memory[key] == 0 for key in ["high", "max", "oom", "oom_kill"])
|
||||
assert observed["resource_events"]["pids.events"]["max"] == 0
|
||||
return {"case": root.name, "question": report["question_kind"],
|
||||
"request_count": len(requests), "whole_request_json_proxy": whole,
|
||||
"whole_request_json_proxy_sum": sum(whole),
|
||||
"rea_namespace_json_proxy": namespaces,
|
||||
"rea_namespace_json_proxy_sum": sum(namespaces),
|
||||
"unescaped_instruction_and_input_text_proxy": texts,
|
||||
"rea_namespace_plus_text_proxy_sum": sum(namespaces) + sum(texts),
|
||||
"wall_seconds": scope["elapsed_seconds"],
|
||||
"family_cpu_seconds": observed["cpu_stat"]["usage_usec"] / 1e6,
|
||||
"family_peak_mib": observed["memory_peak_bytes"] / 2**20,
|
||||
"completed_with_verified_cleanup": True,
|
||||
"sha256": {file.name: hashlib.sha256(file.read_bytes()).hexdigest()
|
||||
for file in [root / "report.json", root / "model-requests.json",
|
||||
root / "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")
|
||||
receipt = {"actual_model_usage": None,
|
||||
"backend": "deterministic local Responses SSE; no AI inference or paid service; not a model-quality or billing test",
|
||||
"client": "Codex app-server 0.163.0", "tiktoken": tiktoken.__version__,
|
||||
"encoding": encoding.name,
|
||||
"records": [inspect(root, encoding) for root in args.cases]}
|
||||
args.output.write_text(json.dumps(receipt, ensure_ascii=False, indent=2) + "\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,292 @@
|
||||
"""Actual Codex wire capture with a deterministic local SSE backend; no AI inference."""
|
||||
import argparse
|
||||
import http.server
|
||||
import threading
|
||||
import asyncio
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import time
|
||||
import traceback
|
||||
import subprocess
|
||||
|
||||
|
||||
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")
|
||||
parser.add_argument("--input", type=Path, required=True)
|
||||
parser.add_argument("--oracle", type=Path, required=True)
|
||||
parser.add_argument("--expected-tools", type=int, default=210)
|
||||
parser.add_argument("--mode", choices=["complete", "selected", "summary"], required=True)
|
||||
parser.add_argument("--export", action="store_true")
|
||||
parser.add_argument("--question", choices=["identity", "dependency"], default="dependency")
|
||||
args = parser.parse_args()
|
||||
assert args.question != "identity" or args.mode != "selected"
|
||||
assert args.mode != "summary" or args.question == "identity"
|
||||
oracle=json.loads(args.oracle.read_text())
|
||||
target=str(args.input)
|
||||
assert args.input.is_absolute() and args.oracle.is_absolute() and args.output.is_absolute()
|
||||
assert hashlib.sha256(args.input.read_bytes()).hexdigest()==oracle["artifact_sha256"]
|
||||
def delivered(pieces):
|
||||
assert isinstance(pieces,str) and pieces.startswith("Wall time: "), pieces[:300]
|
||||
_,sep,payload=pieces.partition("Output:\n");assert sep
|
||||
value=json.loads(payload)
|
||||
return value["result"] if "result" in value and "normalized_result" not in value else value
|
||||
def outputs_of(body):
|
||||
return {item["call_id"]:delivered(item["output"]) for item in body.get("input",[]) if item.get("type")=="function_call_output"}
|
||||
steps=1+(args.mode=="selected")+args.export
|
||||
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)
|
||||
captured = []
|
||||
class Capture(http.server.BaseHTTPRequestHandler):
|
||||
def log_message(self, *_):
|
||||
pass
|
||||
def do_POST(self):
|
||||
size = int(self.headers["Content-Length"])
|
||||
assert 0 < size <= 16 * 1024 * 1024
|
||||
assert self.headers.get("Content-Encoding", "identity") == "identity"
|
||||
body = json.loads(self.rfile.read(size))
|
||||
captured.append({"path": self.path, "body": body})
|
||||
(args.output / "model-requests.json").write_text(json.dumps(captured) + "\n")
|
||||
index = len(captured)
|
||||
names = [t.get("name", "") for ns in body.get("tools", []) if ns.get("type") == "namespace" and ns.get("name") == "mcp__rea_audit" for t in ns.get("tools", [])]
|
||||
assert index <= steps+1
|
||||
response_id=f"resp_local_{index}"
|
||||
if index <= steps:
|
||||
previous=outputs_of(body)
|
||||
if index==1:
|
||||
operation="inspect_go_binary";arguments={"path":target}
|
||||
if args.mode!="complete":arguments["detail"]="summary"
|
||||
elif args.mode=="selected" and index==2:
|
||||
parent=previous["call_local_1"]["normalized_result"]["parent_evidence_id"]
|
||||
operation="inspect_analysis_view";arguments={"source":{"kind":"retained-evidence","evidence_id":parent},"view":{"kind":"go","collection":"dependencies","paths":[oracle["dependency_path"]],"fields":["path","version","sum","replacement"],"offset":0,"limit":64}}
|
||||
else:
|
||||
operation="export_evidence_bundle";arguments={"path":str(args.output/"evidence.json")}
|
||||
selected=[name for name in names if name.endswith(operation)];assert len(selected)==1
|
||||
item={"type":"function_call","id":f"fc_local_{index}","call_id":f"call_local_{index}","name":selected[0],"namespace":"mcp__rea_audit","arguments":json.dumps(arguments),"status":"completed"}
|
||||
else:
|
||||
item={"type":"message","id":"msg_local_done","role":"assistant","status":"completed","content":[{"type":"output_text","text":"Scripted local Go metadata query complete.","annotations":[]}]}
|
||||
base = {"id": response_id, "object": "response", "model": body.get("model"), "status": "completed", "output": [item]}
|
||||
events = [
|
||||
{"type": "response.created", "response": {**base, "status": "in_progress", "output": []}},
|
||||
{"type": "response.output_item.added", "output_index": 0, "item": {**item, "status": "in_progress"}},
|
||||
{"type": "response.output_item.done", "output_index": 0, "item": item},
|
||||
{"type": "response.completed", "response": base},
|
||||
]
|
||||
wire = "".join("event: " + e["type"] + "\ndata: " + json.dumps(e) + "\n\n" for e in events).encode()
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "text/event-stream")
|
||||
self.send_header("Content-Length", str(len(wire)))
|
||||
self.end_headers()
|
||||
self.wfile.write(wire)
|
||||
endpoint = http.server.HTTPServer(("127.0.0.1", 0), Capture)
|
||||
endpoint_thread = threading.Thread(target=endpoint.serve_forever, daemon=True)
|
||||
endpoint_thread.start()
|
||||
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 = 'model_provider = "rea_capture"\nmodel = "rea-wire-probe"\n[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())
|
||||
for name in ['inspect_go_binary','inspect_analysis_view','export_evidence_bundle']:
|
||||
config += f'[mcp_servers.rea_audit.tools.{name}]\napproval_mode = "approve"\noutput_token_limit = 30000\n'
|
||||
config += f'\n[model_providers.rea_capture]\nname = "Local scripted capture"\nbase_url = "http://127.0.0.1:{endpoint.server_port}/v1"\nwire_api = "responses"\nrequires_openai_auth = false\nsupports_websockets = false\nrequest_max_retries = 0\nstream_max_retries = 0\n'
|
||||
(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": "Read embedded Go identity/counts or a selected dependency. Export is a separate task variant.",
|
||||
"client": "Codex app-server 0.163.0", "model_turns_started": 1, "backend": "local deterministic SSE fixture; no AI model or paid service",
|
||||
"actual_model_usage": None, "input_profile": args.input_profile,
|
||||
"output_profile": args.output_profile, "calls": [], "observations": [],
|
||||
"unexpected_server_requests": [], "cleanup_failures": []}
|
||||
client_version = subprocess.check_output([str(args.codex), "--version"], env=environment, cwd=workspace, text=True, timeout=10).strip()
|
||||
assert client_version == "codex-cli 0.163.0", client_version
|
||||
transcript = []
|
||||
pending = {}
|
||||
completed_turn = asyncio.Event()
|
||||
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 value.get("method") == "turn/completed":
|
||||
report["turn"] = value["params"]["turn"]
|
||||
completed_turn.set()
|
||||
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 scripted metadata 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", "turn/start"]
|
||||
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)) == args.expected_tools
|
||||
assert "inspect_go_binary" in names
|
||||
output_schemas = sum(tool.get("outputSchema") is not None for tool in tools)
|
||||
assert output_schemas == (args.expected_tools 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")}
|
||||
await request("turn/start", {"threadId": thread_id, "input": [{"type": "text", "text": ("Read the embedded Go compiler, main module and embedded dependency count; state runtime dependency coverage." if args.question=="identity" else "Read the embedded Go compiler, main module and golang.org/x/crypto dependency and state runtime dependency coverage.")+" The local fixture scripts the required tools; no AI inference is used."}]})
|
||||
await asyncio.wait_for(completed_turn.wait(), 90)
|
||||
assert report["turn"]["status"] == "completed", report["turn"]
|
||||
assert len(captured)==steps+1
|
||||
outputs=outputs_of(captured[-1]["body"])
|
||||
assert len(outputs)==steps
|
||||
initial=outputs["call_local_1"]
|
||||
assert initial["normalized_result"]["artifact"]["sha256"]==oracle["artifact_sha256"]
|
||||
if args.question=="identity":
|
||||
info=initial["normalized_result"]["build_info"] if args.mode=="complete" else initial["normalized_result"]["summary"]["build_info"]
|
||||
count=len(info["module"]["dependencies"]) if args.mode=="complete" else info["module"]["available_counts"]["dependencies"]
|
||||
facts={"go_version":info["go_version"],"main":info["module"]["main"],"runtime_dependencies":"unknown","dependency_count":count}
|
||||
parent=initial["evidence_id"] if args.mode=="complete" else initial["normalized_result"]["parent_evidence_id"]
|
||||
elif args.mode=="complete":
|
||||
info=initial["normalized_result"]["build_info"]
|
||||
facts={"go_version":info["go_version"],"main":info["module"]["main"],"runtime_dependencies":"unknown","dependencies":[{"index":i,**d} for i,d in enumerate(info["module"]["dependencies"]) if d["path"]==oracle["dependency_path"]]}
|
||||
parent=initial["evidence_id"]
|
||||
else:
|
||||
info=initial["normalized_result"]["summary"]["build_info"]
|
||||
selected=outputs["call_local_2"]["normalized_result"]
|
||||
assert selected["coverage"]["exhausted"] and selected["source_coverage"]["runtime_dependencies"]=="unknown"
|
||||
facts={"go_version":info["go_version"],"main":info["module"]["main"],"runtime_dependencies":"unknown","dependencies":selected["page"]["rows"]}
|
||||
parent=initial["normalized_result"]["parent_evidence_id"]
|
||||
expected=({key:oracle["answer"][key] for key in ["go_version","main","runtime_dependencies"]}|{"dependency_count":oracle["dependency_count"]}) if args.question=="identity" else oracle["answer"]
|
||||
assert facts==expected
|
||||
report["question_kind"]=args.question
|
||||
if args.export:
|
||||
bundle=json.loads((args.output/"evidence.json").read_text());evidence=next(e for e in bundle["records"] if e["evidence_id"]==parent)
|
||||
assert evidence["normalized_result"]==json.loads(Path(oracle["complete_result_path"]).read_text())
|
||||
report["observations"]={"answer":facts,"parent_evidence_id":parent,"exported":args.export}
|
||||
report["captured_requests"]=len(captured)
|
||||
report["mode"]=args.mode
|
||||
report["request_tool_counts"] = [len(c["body"].get("tools", [])) for c in captured]
|
||||
assert not report["unexpected_server_requests"]
|
||||
assert hashlib.sha256(args.input.read_bytes()).hexdigest()==oracle["artifact_sha256"]
|
||||
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")
|
||||
endpoint.shutdown()
|
||||
endpoint.server_close()
|
||||
endpoint_thread.join(5)
|
||||
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,435 @@
|
||||
{
|
||||
"baseline_source": "264862bf2e3612c9cb6fcfb861843cbba4e1893f",
|
||||
"candidate_code": "c2550343173c5abd8f34e925eac1bcc12b7ee6a2",
|
||||
"toolchain": {
|
||||
"node": "24.18.0",
|
||||
"npm": "11.16.0"
|
||||
},
|
||||
"runtime_filetables_sha256": {
|
||||
"baseline": "81c293ff8997017cfe9db2a65a8b70b188e3e6d20cef07902599c69ade5d9fac",
|
||||
"candidate": "b4b654a7151f6affbb3ab8541bff74b643d9f78073d338b8feb2b095dd12b6b5"
|
||||
},
|
||||
"lock_sha256": "699a14749e3825c62430faeb18224190fee1c9347760494e38e334e180bea6ec",
|
||||
"gguf_source_sha256": {
|
||||
"baseline": "9c387d5fba10db7830e74ec74a73f4b3d2e1347cca665e948b7891881b186007",
|
||||
"candidate": "c43e5f8eebe04c0f82e0a970606f20ddf409c7a5fd0e298c88b09bcd0e7864a9"
|
||||
},
|
||||
"driver_sha256": "9732f45b8b550eef47c1fc993b435129108934f3ec3501a34df1784e6ee37b44",
|
||||
"resource_guard_sha256": {
|
||||
"resource-run.py": "7a9642ab86ba34290564f636eb080cacf38b4185a254028680b238bff172993b",
|
||||
"resource_payload.py": "6f089e645459eba80c5d73e39c21606c78173a44daf49fabc3b88c4631ce51c6"
|
||||
},
|
||||
"oracle": {
|
||||
"gguf": "0.19.0",
|
||||
"safetensors": "0.8.0",
|
||||
"receipts": [
|
||||
{
|
||||
"file": "stories15M-q4_0.gguf",
|
||||
"sha256": "66967fbece6dbe97886593fdbb73589584927e29119ec31f08090732d1861739",
|
||||
"metadata": 20,
|
||||
"tensors": 57,
|
||||
"status": "matched"
|
||||
},
|
||||
{
|
||||
"file": "stories260K.gguf",
|
||||
"sha256": "270cba1bd5109f42d03350f60406024560464db173c0e387d91f0426d3bd256d",
|
||||
"metadata": 19,
|
||||
"tensors": 48,
|
||||
"status": "matched"
|
||||
},
|
||||
{
|
||||
"file": "tiny-bert.safetensors",
|
||||
"sha256": "965f02b6a7e5520fc12f710e4e3b6132f697f1c8f648819553c5ade86752d2de",
|
||||
"metadata": 2,
|
||||
"tensors": 100,
|
||||
"status": "matched"
|
||||
}
|
||||
]
|
||||
},
|
||||
"tokenizer": "tiktoken 0.12.0 cl100k_base; independent compact JSON chunks, catalog once + each named request/reply; not actual model usage or initialization/framing",
|
||||
"cases": [
|
||||
{
|
||||
"artifact": {
|
||||
"repo": "ggml-org/models",
|
||||
"revision": "499bc8821c6b12b4e53c5bffcb21ec206f212d81",
|
||||
"member": "tinyllamas/stories15M-q4_0.gguf",
|
||||
"file": "stories15M-q4_0.gguf",
|
||||
"url": "https://huggingface.co/ggml-org/models/resolve/499bc8821c6b12b4e53c5bffcb21ec206f212d81/tinyllamas/stories15M-q4_0.gguf",
|
||||
"bytes": 19077344
|
||||
},
|
||||
"normalized_sha256": "a894614da7ce9486b07eb2ede05ce11307d4726089722278bc4c95043feb6b66",
|
||||
"answers_sha256": "64277f01f43440bc99a94ab0787a4ddde22866ed20140d87d3942b1723cbb067",
|
||||
"catalog_sha256": "859b1433ef7c45ea23ba4073715d25ed7a76fd9df6d74285d20a5ea39e8e2898",
|
||||
"mcp": [
|
||||
{
|
||||
"pair": 1,
|
||||
"side": "baseline",
|
||||
"wall_seconds": 6.667,
|
||||
"cpu_seconds": 6.480601,
|
||||
"peak_family_mib": 769.34,
|
||||
"catalog_plus_calls_tokens": 895583,
|
||||
"calls_ms": [367.272, 1936.653, 162.868, 12.072, 19.236]
|
||||
},
|
||||
{
|
||||
"pair": 1,
|
||||
"side": "candidate",
|
||||
"wall_seconds": 6.867,
|
||||
"cpu_seconds": 6.690645,
|
||||
"peak_family_mib": 816.34,
|
||||
"catalog_plus_calls_tokens": 895583,
|
||||
"calls_ms": [324.806, 1822.885, 153.167, 11.988, 18.093]
|
||||
},
|
||||
{
|
||||
"pair": 2,
|
||||
"side": "baseline",
|
||||
"wall_seconds": 6.77,
|
||||
"cpu_seconds": 6.555891,
|
||||
"peak_family_mib": 798.98,
|
||||
"catalog_plus_calls_tokens": 895583,
|
||||
"calls_ms": [456.467, 1823.786, 160.038, 11.296, 18.607]
|
||||
},
|
||||
{
|
||||
"pair": 2,
|
||||
"side": "candidate",
|
||||
"wall_seconds": 6.565,
|
||||
"cpu_seconds": 6.3467,
|
||||
"peak_family_mib": 813.566,
|
||||
"catalog_plus_calls_tokens": 895583,
|
||||
"calls_ms": [329.862, 1767.086, 152.299, 10.948, 17.523]
|
||||
},
|
||||
{
|
||||
"pair": 3,
|
||||
"side": "baseline",
|
||||
"wall_seconds": 6.666,
|
||||
"cpu_seconds": 6.49779,
|
||||
"peak_family_mib": 771.02,
|
||||
"catalog_plus_calls_tokens": 895583,
|
||||
"calls_ms": [345.895, 1854.963, 156.895, 11.476, 15.852]
|
||||
},
|
||||
{
|
||||
"pair": 3,
|
||||
"side": "candidate",
|
||||
"wall_seconds": 6.567,
|
||||
"cpu_seconds": 6.386103,
|
||||
"peak_family_mib": 818.891,
|
||||
"catalog_plus_calls_tokens": 895583,
|
||||
"calls_ms": [342.734, 1827.835, 157.363, 10.122, 17.6]
|
||||
}
|
||||
],
|
||||
"phase": [
|
||||
{
|
||||
"pair": 1,
|
||||
"side": "baseline",
|
||||
"first_ms": 154.027,
|
||||
"repeat_median_ms": 56.14,
|
||||
"wall_seconds": 1.028,
|
||||
"cpu_seconds": 0.87953,
|
||||
"peak_family_mib": 75.867
|
||||
},
|
||||
{
|
||||
"pair": 1,
|
||||
"side": "candidate",
|
||||
"first_ms": 121.41,
|
||||
"repeat_median_ms": 35.429,
|
||||
"wall_seconds": 0.925,
|
||||
"cpu_seconds": 0.717987,
|
||||
"peak_family_mib": 77.945
|
||||
},
|
||||
{
|
||||
"pair": 2,
|
||||
"side": "baseline",
|
||||
"first_ms": 159.766,
|
||||
"repeat_median_ms": 53.575,
|
||||
"wall_seconds": 1.027,
|
||||
"cpu_seconds": 0.854739,
|
||||
"peak_family_mib": 75.586
|
||||
},
|
||||
{
|
||||
"pair": 2,
|
||||
"side": "candidate",
|
||||
"first_ms": 121.383,
|
||||
"repeat_median_ms": 38.773,
|
||||
"wall_seconds": 0.925,
|
||||
"cpu_seconds": 0.733397,
|
||||
"peak_family_mib": 75.125
|
||||
},
|
||||
{
|
||||
"pair": 3,
|
||||
"side": "baseline",
|
||||
"first_ms": 148.447,
|
||||
"repeat_median_ms": 53.58,
|
||||
"wall_seconds": 1.026,
|
||||
"cpu_seconds": 0.848173,
|
||||
"peak_family_mib": 75.363
|
||||
},
|
||||
{
|
||||
"pair": 3,
|
||||
"side": "candidate",
|
||||
"first_ms": 121.712,
|
||||
"repeat_median_ms": 37.191,
|
||||
"wall_seconds": 0.926,
|
||||
"cpu_seconds": 0.728712,
|
||||
"peak_family_mib": 77.887
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"artifact": {
|
||||
"repo": "ggml-org/models",
|
||||
"revision": "499bc8821c6b12b4e53c5bffcb21ec206f212d81",
|
||||
"member": "tinyllamas/stories260K.gguf",
|
||||
"file": "stories260K.gguf",
|
||||
"url": "https://huggingface.co/ggml-org/models/resolve/499bc8821c6b12b4e53c5bffcb21ec206f212d81/tinyllamas/stories260K.gguf",
|
||||
"bytes": 1185376
|
||||
},
|
||||
"normalized_sha256": "15f4dbdc30dd9bd936747057031c6391c38b4c1717d56ae33ed2f0cba4184405",
|
||||
"answers_sha256": "99257428a01aa513b435c95ca7217c69f4641d9021372a6333e7cb9e2cb35d63",
|
||||
"catalog_sha256": "859b1433ef7c45ea23ba4073715d25ed7a76fd9df6d74285d20a5ea39e8e2898",
|
||||
"mcp": [
|
||||
{
|
||||
"pair": 1,
|
||||
"side": "baseline",
|
||||
"wall_seconds": 6.568,
|
||||
"cpu_seconds": 6.375324,
|
||||
"peak_family_mib": 743.887,
|
||||
"catalog_plus_calls_tokens": 895421,
|
||||
"calls_ms": [223.11, 1938.73, 102.018, 10.759, 16.11]
|
||||
},
|
||||
{
|
||||
"pair": 1,
|
||||
"side": "candidate",
|
||||
"wall_seconds": 6.566,
|
||||
"cpu_seconds": 6.338327,
|
||||
"peak_family_mib": 744.852,
|
||||
"catalog_plus_calls_tokens": 895421,
|
||||
"calls_ms": [248.979, 1886.809, 105.801, 10.738, 15.653]
|
||||
},
|
||||
{
|
||||
"pair": 2,
|
||||
"side": "baseline",
|
||||
"wall_seconds": 6.365,
|
||||
"cpu_seconds": 6.206216,
|
||||
"peak_family_mib": 783.84,
|
||||
"catalog_plus_calls_tokens": 895421,
|
||||
"calls_ms": [248.196, 1845.737, 112.874, 10.724, 17.594]
|
||||
},
|
||||
{
|
||||
"pair": 2,
|
||||
"side": "candidate",
|
||||
"wall_seconds": 7.383,
|
||||
"cpu_seconds": 7.211823,
|
||||
"peak_family_mib": 742.281,
|
||||
"catalog_plus_calls_tokens": 895421,
|
||||
"calls_ms": [233.155, 1909.721, 104.803, 11.603, 19.357]
|
||||
},
|
||||
{
|
||||
"pair": 3,
|
||||
"side": "baseline",
|
||||
"wall_seconds": 6.565,
|
||||
"cpu_seconds": 6.323702,
|
||||
"peak_family_mib": 740.84,
|
||||
"catalog_plus_calls_tokens": 895421,
|
||||
"calls_ms": [233.664, 1918.301, 104.389, 14.561, 15.227]
|
||||
},
|
||||
{
|
||||
"pair": 3,
|
||||
"side": "candidate",
|
||||
"wall_seconds": 6.566,
|
||||
"cpu_seconds": 6.315785,
|
||||
"peak_family_mib": 778.133,
|
||||
"catalog_plus_calls_tokens": 895421,
|
||||
"calls_ms": [232.37, 1851.929, 78.318, 21.857, 16.438]
|
||||
}
|
||||
],
|
||||
"phase": [
|
||||
{
|
||||
"pair": 1,
|
||||
"side": "baseline",
|
||||
"first_ms": 35.366,
|
||||
"repeat_median_ms": 7.784,
|
||||
"wall_seconds": 0.625,
|
||||
"cpu_seconds": 0.429065,
|
||||
"peak_family_mib": 62.922
|
||||
},
|
||||
{
|
||||
"pair": 1,
|
||||
"side": "candidate",
|
||||
"first_ms": 30.009,
|
||||
"repeat_median_ms": 7.934,
|
||||
"wall_seconds": 0.624,
|
||||
"cpu_seconds": 0.392848,
|
||||
"peak_family_mib": 59.664
|
||||
},
|
||||
{
|
||||
"pair": 2,
|
||||
"side": "baseline",
|
||||
"first_ms": 32.0,
|
||||
"repeat_median_ms": 8.772,
|
||||
"wall_seconds": 0.624,
|
||||
"cpu_seconds": 0.412059,
|
||||
"peak_family_mib": 60.422
|
||||
},
|
||||
{
|
||||
"pair": 2,
|
||||
"side": "candidate",
|
||||
"first_ms": 32.032,
|
||||
"repeat_median_ms": 9.36,
|
||||
"wall_seconds": 0.624,
|
||||
"cpu_seconds": 0.412412,
|
||||
"peak_family_mib": 59.441
|
||||
},
|
||||
{
|
||||
"pair": 3,
|
||||
"side": "baseline",
|
||||
"first_ms": 33.628,
|
||||
"repeat_median_ms": 9.496,
|
||||
"wall_seconds": 0.624,
|
||||
"cpu_seconds": 0.396232,
|
||||
"peak_family_mib": 60.41
|
||||
},
|
||||
{
|
||||
"pair": 3,
|
||||
"side": "candidate",
|
||||
"first_ms": 30.506,
|
||||
"repeat_median_ms": 8.172,
|
||||
"wall_seconds": 0.622,
|
||||
"cpu_seconds": 0.404398,
|
||||
"peak_family_mib": 61.672
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"artifact": {
|
||||
"repo": "hf-internal-testing/tiny-random-bert",
|
||||
"revision": "f171d7baecaf37b5da5a3616d8833b9969753535",
|
||||
"member": "model.safetensors",
|
||||
"file": "tiny-bert.safetensors",
|
||||
"url": "https://huggingface.co/hf-internal-testing/tiny-random-bert/resolve/f171d7baecaf37b5da5a3616d8833b9969753535/model.safetensors",
|
||||
"bytes": 520212
|
||||
},
|
||||
"normalized_sha256": "8658d90261cdbaaf1f5f8d2e9408ad301b40ad3e6b7aab7ac9c5d4be71ca36d5",
|
||||
"answers_sha256": "82072404a0aa24e3b42979397421f455da2589c2ea649f28ba7279d43e6c3bc8",
|
||||
"catalog_sha256": "859b1433ef7c45ea23ba4073715d25ed7a76fd9df6d74285d20a5ea39e8e2898",
|
||||
"mcp": [
|
||||
{
|
||||
"pair": 1,
|
||||
"side": "baseline",
|
||||
"wall_seconds": 6.667,
|
||||
"cpu_seconds": 6.46146,
|
||||
"peak_family_mib": 741.602,
|
||||
"catalog_plus_calls_tokens": 893755,
|
||||
"calls_ms": [251.201, 1965.785, 114.055, 10.837, 28.862]
|
||||
},
|
||||
{
|
||||
"pair": 1,
|
||||
"side": "candidate",
|
||||
"wall_seconds": 6.767,
|
||||
"cpu_seconds": 6.519606,
|
||||
"peak_family_mib": 736.367,
|
||||
"catalog_plus_calls_tokens": 893755,
|
||||
"calls_ms": [255.557, 1979.227, 86.726, 10.093, 24.531]
|
||||
},
|
||||
{
|
||||
"pair": 2,
|
||||
"side": "baseline",
|
||||
"wall_seconds": 6.565,
|
||||
"cpu_seconds": 6.379249,
|
||||
"peak_family_mib": 730.273,
|
||||
"catalog_plus_calls_tokens": 893755,
|
||||
"calls_ms": [245.517, 1900.802, 94.351, 10.0, 25.635]
|
||||
},
|
||||
{
|
||||
"pair": 2,
|
||||
"side": "candidate",
|
||||
"wall_seconds": 6.569,
|
||||
"cpu_seconds": 6.388131,
|
||||
"peak_family_mib": 785.746,
|
||||
"catalog_plus_calls_tokens": 893755,
|
||||
"calls_ms": [250.715, 1846.568, 103.314, 21.618, 27.261]
|
||||
},
|
||||
{
|
||||
"pair": 3,
|
||||
"side": "baseline",
|
||||
"wall_seconds": 6.771,
|
||||
"cpu_seconds": 6.530512,
|
||||
"peak_family_mib": 745.098,
|
||||
"catalog_plus_calls_tokens": 893755,
|
||||
"calls_ms": [255.441, 1949.189, 110.897, 10.679, 24.345]
|
||||
},
|
||||
{
|
||||
"pair": 3,
|
||||
"side": "candidate",
|
||||
"wall_seconds": 6.466,
|
||||
"cpu_seconds": 6.296937,
|
||||
"peak_family_mib": 741.348,
|
||||
"catalog_plus_calls_tokens": 893755,
|
||||
"calls_ms": [243.919, 1895.978, 113.995, 10.66, 27.269]
|
||||
}
|
||||
],
|
||||
"phase": [
|
||||
{
|
||||
"pair": 1,
|
||||
"side": "baseline",
|
||||
"first_ms": 27.648,
|
||||
"repeat_median_ms": 8.11,
|
||||
"wall_seconds": 0.623,
|
||||
"cpu_seconds": 0.394405,
|
||||
"peak_family_mib": 57.281
|
||||
},
|
||||
{
|
||||
"pair": 1,
|
||||
"side": "candidate",
|
||||
"first_ms": 27.003,
|
||||
"repeat_median_ms": 8.082,
|
||||
"wall_seconds": 0.523,
|
||||
"cpu_seconds": 0.383678,
|
||||
"peak_family_mib": 57.266
|
||||
},
|
||||
{
|
||||
"pair": 2,
|
||||
"side": "baseline",
|
||||
"first_ms": 26.941,
|
||||
"repeat_median_ms": 8.04,
|
||||
"wall_seconds": 0.626,
|
||||
"cpu_seconds": 0.401321,
|
||||
"peak_family_mib": 59.504
|
||||
},
|
||||
{
|
||||
"pair": 2,
|
||||
"side": "candidate",
|
||||
"first_ms": 28.012,
|
||||
"repeat_median_ms": 8.006,
|
||||
"wall_seconds": 0.625,
|
||||
"cpu_seconds": 0.392541,
|
||||
"peak_family_mib": 59.012
|
||||
},
|
||||
{
|
||||
"pair": 3,
|
||||
"side": "baseline",
|
||||
"first_ms": 28.734,
|
||||
"repeat_median_ms": 8.499,
|
||||
"wall_seconds": 0.624,
|
||||
"cpu_seconds": 0.411118,
|
||||
"peak_family_mib": 59.23
|
||||
},
|
||||
{
|
||||
"pair": 3,
|
||||
"side": "candidate",
|
||||
"first_ms": 29.961,
|
||||
"repeat_median_ms": 8.632,
|
||||
"wall_seconds": 0.625,
|
||||
"cpu_seconds": 0.41875,
|
||||
"peak_family_mib": 57.5
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"published_equivalent_source": "2f091ccc734d7bd945bd61432d41cf5ba68be12b",
|
||||
"candidate_reproduction": "Use baseline_source plus the byte-identical src/model/GgufHeader.ts from published_equivalent_source; driver SHA-256 is pinned separately. Measured local candidate predates the integration rebase.",
|
||||
"post_review_receipt_fix": {
|
||||
"measured_driver_version": "2f091ccc734d7bd945bd61432d41cf5ba68be12b",
|
||||
"driver_sha256": "449860fb973418f2e3e51183a81be1e3111ffa6453de8208a4b175d3d410322f",
|
||||
"publication_owner_sha256": "901092ae6dc7fda1a8869d62362a0a9f560e03d5ef5667a5c6ed660db000b9a2",
|
||||
"validation": "3 new fault-injection regressions reject original driver and pass current; 11 shared receipt regressions pass. Real stories15M MCP and seven-inspection phase reruns qualify; original paired samples retain their measured driver hash."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
# Omitted GGUF string validation
|
||||
|
||||
[Issue #5774](https://github.com/morluto/rea/issues/5774) follows the existing
|
||||
real-model and completed-task audit in [#5603](https://github.com/morluto/rea/pull/5603).
|
||||
The [coverage reconciliation](coverage-review-20261021.md) explains why neither
|
||||
model inspection nor IL2CPP, firmware extraction or IDA should be described as
|
||||
wholly unaudited. This experiment changes only one model producer hot path.
|
||||
|
||||
## Owner and preserved semantics
|
||||
|
||||
GGUF arrays retain eight preview values. Previously, each omitted string still
|
||||
constructed a fatal UTF-8 decoder and materialized discarded text. `GgufHeader`
|
||||
now validates an omitted value of at most one existing 65,536-byte chunk with
|
||||
Node's `isUtf8`. Longer values retain the streaming fatal decoder, including
|
||||
multi-byte characters split across chunks. Retained values use their original
|
||||
decoder, preserving BOMs and exact text.
|
||||
|
||||
Every admitted byte is still read, hashed and validated. Header bounds,
|
||||
representation admission, cancellation checks, array-loop fairness, source
|
||||
offsets, partial-progress semantics, preview omission and complete retained
|
||||
Evidence are unchanged. There is no cache, new contract, selector or default.
|
||||
CLI and MCP share this producer through the existing application workflow.
|
||||
|
||||
## Corpus and correctness
|
||||
|
||||
The [curated receipts](model-utf8-validation.json) pin source commits, runtime
|
||||
file tables, driver/resource sources, immutable public URLs, artifact digests,
|
||||
complete report/answer fingerprints and all final samples. Baseline is main
|
||||
`264862bf2e3612c9cb6fcfb861843cbba4e1893f`; measured candidate code is
|
||||
`c2550343173c5abd8f34e925eac1bcc12b7ee6a2`. Later documentation/integration
|
||||
commits do not relabel that cohort. That measured local commit predates the
|
||||
rebase: its production file is byte-identical to published
|
||||
`2f091ccc734d7bd945bd61432d41cf5ba68be12b`, with both source hashes recorded.
|
||||
|
||||
| Artifact | Bytes | Metadata / tensors | Independent reader |
|
||||
| ------------------------------------------------ | ---------: | -----------------: | ------------------ |
|
||||
| ggml-org stories15M Q4_0 GGUF | 19,077,344 | 20 / 57 | gguf 0.19.0 |
|
||||
| ggml-org stories260K GGUF | 1,185,376 | 19 / 48 | gguf 0.19.0 |
|
||||
| hf-internal-testing tiny-random-bert safetensors | 520,212 | 2 / 100 | safetensors 0.8.0 |
|
||||
|
||||
Official independent readers match all exposed metadata values/array previews
|
||||
and counts, tensor names, types, shapes and parameter counts. GGUF reader
|
||||
offsets/lengths match every tensor; safetensors reader geometry and independent
|
||||
Python JSON offsets match its file ranges. Python byte hashing checks every
|
||||
header digest. No weights are executed or loaded into a model framework.
|
||||
The main GGUF has 32,000 vocabulary entries; the smaller GGUF and safetensors
|
||||
are controls, not evidence for larger vocabularies or other GGUF versions.
|
||||
|
||||
## Completed public tasks
|
||||
|
||||
Linux x64, Node 24.18.0/npm 11.16.0. Three alternating baseline/candidate,
|
||||
candidate/baseline, baseline/candidate pairs per artifact run serially. Each
|
||||
fresh production stdio MCP task discovers all 212 tools, directly selects two
|
||||
metadata records, retrieves two exact tensors from retained Evidence, repeats
|
||||
both calls, exports complete Evidence, validates answers and closes the
|
||||
transport. OS caches are unflushed; the controller reads inputs for hashing.
|
||||
Fresh process does not mean cold disk or cold model-engine analysis.
|
||||
|
||||
Medians of three whole tasks per side, including SDK/catalog/schema validation,
|
||||
all five calls, source checks, export and teardown:
|
||||
|
||||
| Artifact | Wall seconds, before → after | Family CPU seconds, before → after | Peak family MiB, before → after |
|
||||
| ----------- | ---------------------------: | ---------------------------------: | ------------------------------: |
|
||||
| stories15M | 6.667 → 6.567 | 6.498 → 6.386 | 771.0 → 816.3 |
|
||||
| stories260K | 6.565 → 6.566 | 6.324 → 6.338 | 743.9 → 744.9 |
|
||||
| tiny BERT | 6.667 → 6.569 | 6.461 → 6.388 | 741.6 → 741.3 |
|
||||
|
||||
The primary task's observed wall reduction is about 1.5%, but the unchanged
|
||||
safetensors control has similar variation. No stable whole-task speedup is
|
||||
established. Main-model peak memory rises: ranges 769.3–799.0 → 813.6–818.9
|
||||
MiB; its cause is unmeasured. No peak-memory improvement is claimed.
|
||||
|
||||
Separate fresh-process provider cohorts perform one first inspection and six
|
||||
repeated inspections, comparing the complete report after every operation:
|
||||
|
||||
| Artifact | First / repeat median ms, before | First / repeat median ms, after |
|
||||
| ----------- | -------------------------------: | ------------------------------: |
|
||||
| stories15M | 154.03 / 53.58 | 121.41 / 37.19 |
|
||||
| stories260K | 33.63 / 8.77 | 30.51 / 8.17 |
|
||||
| tiny BERT | 27.65 / 8.11 | 28.01 / 8.08 |
|
||||
|
||||
For stories15M this is about 21% faster first inspection and 31% faster repeated
|
||||
inspection. Median CPU for the seven-inspection process is 0.855 → 0.729 s;
|
||||
peak family memory is 75.6 → 77.9 MiB. Phase gains do not include discovery
|
||||
or substitute for the complete-task results above.
|
||||
|
||||
One catalog plus all named request/reply JSON chunks estimates median
|
||||
895,583 / 895,421 / 893,755 `cl100k_base` tokens for the three artifacts in
|
||||
both implementations, using tiktoken 0.12.0. This includes serialized output
|
||||
schemas, not merely named input schemas. It is an SDK serialization proxy;
|
||||
actual model usage, repeated host injection, negotiation and framing are
|
||||
unmeasured. No token saving is claimed.
|
||||
|
||||
All 18 public tasks and 18 provider processes completed under one CPU, 4 GiB
|
||||
hard / 3 GiB early-stop family memory, no swap and 192 tasks. Payload exit,
|
||||
counters and owned cleanup are verified; no limit/OOM events occurred. Kernel
|
||||
cgroup peak includes file cache. Terminal whole-scope counters after scope
|
||||
removal remain unknown. Raw inputs, exports, profiles and transcripts stay
|
||||
outside Git.
|
||||
|
||||
## Reproduction and validation boundaries
|
||||
|
||||
Build the baseline and the same baseline with the published GGUF source file
|
||||
using the pinned toolchain; this reconstructs the measured local candidate.
|
||||
Capture a complete
|
||||
baseline normalized report from existing CLI/MCP Evidence using the same
|
||||
absolute input path. Run `model-utf8-validation.mjs` with absolute `--runtime`,
|
||||
`--artifact`, `--expected` and a new `--output` directory under `resource-run.py
|
||||
--observe-payload-exit`. Add `--phase` for the seven-inspection provider probe.
|
||||
The driver validates advertised named schemas through the actual SDK and
|
||||
compares full exported parents, preview facts and tensor selections. A failing
|
||||
receipt or cleanup invalidates a task; it is not silently retried as success.
|
||||
|
||||
Five new producer regressions cover empty omitted strings, exact-chunk strings,
|
||||
split multi-byte characters, both byte orders, retained BOMs and rejection of
|
||||
short/long malformed omitted values. Existing malformed-header, admission,
|
||||
cancellation, partial-progress and public CLI/MCP tests remain required.
|
||||
An initial new-test assertion incorrectly hashed alignment padding; the
|
||||
corrected oracle uses the actual declared header, matching existing semantics.
|
||||
The first test launcher refused built projects; the exact built-project gate
|
||||
was then used. Neither attempt is a passing check. The initial resource-wrapper
|
||||
copy failed source-hash lookup before launching a build; corrected guarded
|
||||
builds qualified before measurement.
|
||||
|
||||
Integrated onto main `71b696582`: all 68 tests pass again, and three real public
|
||||
CLI complete reports plus three production SDK tasks match the original
|
||||
normalized reports. Typecheck, lint/module boundaries, formatting, metadata,
|
||||
runtime build, docs checks (five tests) and separate architecture guards pass.
|
||||
`npm run check` does **not** pass: Knip reports an unlisted `g++` invocation in
|
||||
the Windows test added by main's #5772. An independent main source checkout,
|
||||
with matching compiled worker artifacts and an uncached Knip run, reproduces
|
||||
only that finding. It is outside this change and is left unchanged. Initial
|
||||
control attempts lacked resolvable worker artifacts and are not clean controls.
|
||||
Development builds/checks used separate 6/8 GiB hard guards; runtime experiments
|
||||
retain their 4 GiB limit. No guard stop or OOM occurred.
|
||||
|
||||
Greptile identified a receipt defect in the phase driver, not the model parser:
|
||||
failed inspections/assertions were not recorded before cleanup, and a close
|
||||
failure could suppress publication. Each attempted phase now records its cost
|
||||
and primary failure before cleanup. Phase and MCP paths share one atomic receipt
|
||||
publisher; cleanup failures are independent facts and cannot replace the primary
|
||||
failure. Three new fault-injection tests fail on the original driver and pass
|
||||
after the correction; all 11 existing shared receipt tests pass. These stand-ins
|
||||
qualify failure accounting only. Real stories15M MCP and seven-inspection phase
|
||||
reruns also pass with verified counters/cleanup. The original 36 paired samples
|
||||
keep their measured driver hash; the receipt records the post-review driver and
|
||||
publication-owner hashes separately. No timing cohort is relabeled.
|
||||
@@ -0,0 +1,243 @@
|
||||
/** Equivalent model tasks; inputs, expected reports and raw 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 { pathToFileURL } from "node:url";
|
||||
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,
|
||||
publishRetainedQueryReceipt,
|
||||
} from "./retained-query-finalization.mjs";
|
||||
|
||||
const { values } = parseArgs({
|
||||
options: {
|
||||
...Object.fromEntries(
|
||||
["runtime", "artifact", "expected", "output"].map((name) => [
|
||||
name,
|
||||
{ type: "string" },
|
||||
]),
|
||||
),
|
||||
phase: { type: "boolean", default: false },
|
||||
},
|
||||
});
|
||||
for (const name of ["runtime", "artifact", "expected", "output"])
|
||||
assert.ok(values[name] && values[name] === resolve(values[name]), name);
|
||||
const hash = (value) => createHash("sha256").update(value).digest("hex");
|
||||
const expected = JSON.parse(await readFile(values.expected, "utf8"));
|
||||
assert.equal(expected.artifact.path, values.artifact);
|
||||
await mkdir(values.output, { mode: 0o700 });
|
||||
const report = {
|
||||
artifact_sha256: hash(await readFile(values.artifact)),
|
||||
normalized_sha256: hash(JSON.stringify(expected)),
|
||||
calls: [],
|
||||
answers: [],
|
||||
};
|
||||
|
||||
if (values.phase) {
|
||||
const { ModelArtifactProvider } = await import(
|
||||
pathToFileURL(join(values.runtime, "dist/model/ModelArtifactProvider.js"))
|
||||
);
|
||||
const provider = new ModelArtifactProvider();
|
||||
try {
|
||||
for (let index = 0; index < 7; index++) {
|
||||
const sample = {
|
||||
phase: index === 0 ? "fresh" : "repeat",
|
||||
is_error: true,
|
||||
};
|
||||
const start = performance.now();
|
||||
try {
|
||||
const result = await provider.inspect({
|
||||
path: values.artifact,
|
||||
hash: "header",
|
||||
});
|
||||
sample.elapsed_ms = performance.now() - start;
|
||||
assert.ok(result.ok, JSON.stringify(result));
|
||||
assert.deepEqual(result.value, expected);
|
||||
sample.is_error = false;
|
||||
} catch (cause) {
|
||||
sample.elapsed_ms ??= performance.now() - start;
|
||||
sample.failure = String(cause?.stack ?? cause);
|
||||
throw cause;
|
||||
} finally {
|
||||
report.calls.push(sample);
|
||||
}
|
||||
}
|
||||
report.status = "passed";
|
||||
} catch (cause) {
|
||||
report.status = "failed";
|
||||
report.failure = String(cause?.stack ?? cause);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
report.cleanup_failures = [];
|
||||
try {
|
||||
await provider.close();
|
||||
} catch (cause) {
|
||||
report.cleanup_failures.push(`provider: ${String(cause)}`);
|
||||
report.status = "failed";
|
||||
}
|
||||
if (!(await publishRetainedQueryReceipt({ report, output: values.output })))
|
||||
process.exitCode = 1;
|
||||
}
|
||||
} else {
|
||||
await runMcpTask();
|
||||
}
|
||||
|
||||
async function runMcpTask() {
|
||||
const account = await mkdtemp(join(tmpdir(), "rea-model-utf8-"));
|
||||
const client = new Client({ name: "model-utf8-validation", version: "1" });
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [join(values.runtime, "scripts/rea.mjs"), "mcp"],
|
||||
env: {
|
||||
HOME: account,
|
||||
PATH: process.env.PATH,
|
||||
NODE_OPTIONS: "--max-old-space-size=2048",
|
||||
UV_THREADPOOL_SIZE: "1",
|
||||
REA_LOG_LEVEL: "silent",
|
||||
TMPDIR: account,
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
const serverLog = createWriteStream(join(values.output, "server.log"));
|
||||
const serverLogFinished = finished(serverLog);
|
||||
serverLogFinished.catch(() => {});
|
||||
transport.stderr?.pipe(serverLog);
|
||||
const transcript = [];
|
||||
const call = createRetainedQueryCaller(client, (receipt, event) => {
|
||||
report.calls.push(receipt);
|
||||
transcript.push(event);
|
||||
});
|
||||
const observe = async (name, input, phase) => {
|
||||
const reply = await call(name, input, phase);
|
||||
assert.notEqual(reply.isError, true, JSON.stringify(reply));
|
||||
assert.ok(reply.structuredContent);
|
||||
return reply.structuredContent;
|
||||
};
|
||||
try {
|
||||
await client.connect(transport);
|
||||
const catalog = await client.listTools();
|
||||
assert.equal(catalog.nextCursor, undefined);
|
||||
await writeFile(
|
||||
join(values.output, "catalog.json"),
|
||||
JSON.stringify(catalog),
|
||||
);
|
||||
report.catalog_sha256 = hash(JSON.stringify(catalog));
|
||||
report.catalog_tools = catalog.tools.length;
|
||||
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),
|
||||
});
|
||||
const keys = [
|
||||
...new Set(expected.metadata.slice(0, 2).map((row) => row.key)),
|
||||
];
|
||||
const names = expected.tensors.slice(0, 2).map((row) => row.name);
|
||||
const fields = [
|
||||
"key",
|
||||
"type",
|
||||
"value",
|
||||
"element_count",
|
||||
"preview_complete",
|
||||
];
|
||||
const tensorFields = [
|
||||
"name",
|
||||
"type",
|
||||
"shape",
|
||||
"file_offset",
|
||||
"byte_length",
|
||||
];
|
||||
const select = (rows, selectedFields) =>
|
||||
rows.map((row) =>
|
||||
Object.fromEntries(
|
||||
["index", ...selectedFields].map((key) => [key, row[key]]),
|
||||
),
|
||||
);
|
||||
const observations = [];
|
||||
for (const phase of ["fresh", "repeat"]) {
|
||||
const metadata = await observe(
|
||||
"inspect_model_artifact",
|
||||
{
|
||||
path: values.artifact,
|
||||
view: { collection: "metadata", keys, fields },
|
||||
},
|
||||
phase,
|
||||
);
|
||||
const parentId = metadata.normalized_result.parent_evidence_id;
|
||||
assert.equal(typeof parentId, "string");
|
||||
assert.deepEqual(
|
||||
metadata.normalized_result.items,
|
||||
select(
|
||||
expected.metadata.filter((row) => keys.includes(row.key)),
|
||||
fields,
|
||||
),
|
||||
);
|
||||
const tensors = await observe(
|
||||
"inspect_analysis_view",
|
||||
{
|
||||
source: { kind: "retained-evidence", evidence_id: parentId },
|
||||
view: {
|
||||
kind: "model-artifact",
|
||||
collection: "tensors",
|
||||
names,
|
||||
fields: tensorFields,
|
||||
},
|
||||
},
|
||||
`${phase}:tensors`,
|
||||
);
|
||||
assert.equal(tensors.normalized_result.parent_evidence_id, parentId);
|
||||
assert.deepEqual(
|
||||
tensors.normalized_result.items,
|
||||
select(expected.tensors.slice(0, 2), tensorFields),
|
||||
);
|
||||
observations.push(parentId);
|
||||
report.answers.push({
|
||||
parent_evidence_id: parentId,
|
||||
metadata: metadata.normalized_result.items,
|
||||
tensors: tensors.normalized_result.items,
|
||||
});
|
||||
}
|
||||
await observe(
|
||||
"export_evidence_bundle",
|
||||
{ path: join(values.output, "evidence.json") },
|
||||
"export",
|
||||
);
|
||||
const bundle = JSON.parse(
|
||||
await readFile(join(values.output, "evidence.json"), "utf8"),
|
||||
);
|
||||
for (const id of observations) {
|
||||
const parent = bundle.records.find((record) => record.evidence_id === id);
|
||||
assert.ok(parent);
|
||||
assert.deepEqual(parent.normalized_result, expected);
|
||||
}
|
||||
assert.deepEqual(report.answers[0], report.answers[1]);
|
||||
assert.equal(hash(await readFile(values.artifact)), report.artifact_sha256);
|
||||
report.status = "passed";
|
||||
} catch (cause) {
|
||||
report.status = "failed";
|
||||
report.failure = String(cause?.stack ?? cause);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
if (
|
||||
!(await finalizeRetainedQueryReceipt({
|
||||
report,
|
||||
client,
|
||||
transport,
|
||||
account,
|
||||
serverLog,
|
||||
serverLogFinished,
|
||||
output: values.output,
|
||||
transcript,
|
||||
}))
|
||||
)
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ Current history and the existing evidence support these narrower conclusions:
|
||||
| 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. |
|
||||
| IDA | Existing [native Windows verification](ida-native-windows.md) includes selected real completed-task performance and correctness/authority checks. It does not qualify every operation or platform. |
|
||||
| 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. |
|
||||
|
||||
@@ -40,6 +40,7 @@ export const finalizeRetainedQueryReceipt = async ({
|
||||
serverLog,
|
||||
serverLogFinished,
|
||||
output,
|
||||
transcript,
|
||||
stdout = process.stdout,
|
||||
}) => {
|
||||
report.cleanup_failures = [];
|
||||
@@ -57,7 +58,21 @@ export const finalizeRetainedQueryReceipt = async ({
|
||||
);
|
||||
if (report.cleanup_failures.length > 0) report.retained_workspace = account;
|
||||
|
||||
return publishRetainedQueryReceipt({ report, output, transcript, stdout });
|
||||
};
|
||||
|
||||
/** Share atomic publication and failure accounting with non-MCP phase probes. */
|
||||
export const publishRetainedQueryReceipt = async ({
|
||||
report,
|
||||
output,
|
||||
transcript,
|
||||
stdout = process.stdout,
|
||||
}) => {
|
||||
report.receipt_failures = [];
|
||||
if (transcript !== undefined)
|
||||
await attempt(report.receipt_failures, "transcript-file", () =>
|
||||
writeFile(join(output, "transcript.json"), JSON.stringify(transcript)),
|
||||
);
|
||||
let publication = 0;
|
||||
const saveReport = async () => {
|
||||
// Keep failed publications in the owned output directory for inspection.
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
"""Count completed Rust questions; tokenizer estimates are not model usage."""
|
||||
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 {}
|
||||
events = observed.get("resource_events", {})
|
||||
memory = events.get("memory.events", {})
|
||||
assert (report["status"] == "passed" 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 observed.get("payload_cleanup_verified") is True
|
||||
and observed.get("payload_exit_code") == 0
|
||||
and all(memory.get(key) == 0 for key in ["high", "max", "oom", "oom_kill"])
|
||||
and events.get("pids.events", {}).get("max") == 0), path
|
||||
assert [event["name"] for event in transcript] == [
|
||||
"inspect_rust_binary", "export_evidence_bundle"]
|
||||
count = lambda value: len(encoding.encode(value, disallowed_special=()))
|
||||
schema_fields = ["name", "description", "inputSchema"]
|
||||
tool_tokens = lambda tools: count(compact([
|
||||
{key: tool[key] for key in schema_fields} for tool in tools]))
|
||||
inputs = tool_tokens(catalog["tools"])
|
||||
selected = tool_tokens([tool for tool in catalog["tools"] if tool["name"] in [
|
||||
"inspect_rust_binary", "export_evidence_bundle"]])
|
||||
arguments = sum(count(compact({"name": event["name"], "arguments": event["input"]}))
|
||||
for event in transcript)
|
||||
replies = sum(count(block["text"]) for event in transcript
|
||||
for block in event["reply"]["content"] if block["type"] == "text")
|
||||
return {"case": path.name, "lane": report["lane"],
|
||||
"answer": report["answer"], "parent_evidence_id": report["parent_evidence_id"],
|
||||
"artifact_sha256": report["artifact_sha256"],
|
||||
"complete_result_sha256": report["normalized_sha256"],
|
||||
"catalog_sha256": report["catalog_sha256"],
|
||||
"catalog_tools": report["catalog_tools"], "calls": report["calls"],
|
||||
"wall_seconds": scope["elapsed_seconds"],
|
||||
"cpu_seconds": observed["cpu_stat"]["usage_usec"] / 1e6,
|
||||
"kernel_family_peak_bytes": observed["memory_peak_bytes"],
|
||||
"events_at_payload_exit": events,
|
||||
"final_whole_scope_events": scope.get("resource_events_final"),
|
||||
"completed_with_verified_cleanup": True,
|
||||
"token_proxy": {"input_catalog_once": inputs, "arguments": arguments,
|
||||
"text_replies": replies, "combined": inputs + arguments + replies,
|
||||
"only_two_selected_tools": selected + arguments + replies,
|
||||
"complete_transport_catalog": count(compact(catalog))},
|
||||
"sha256": {file.name: hashlib.sha256(file.read_bytes()).hexdigest()
|
||||
for file 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")
|
||||
receipt = {"tiktoken": tiktoken.__version__, "encoding": encoding.name,
|
||||
"actual_model_usage": None,
|
||||
"projection": "All discovered tool descriptions/input schemas once, plus every call argument and text reply including export. Selected-two-tool and complete transport catalog costs are separate. No model, host injection, history replay or retry observed.",
|
||||
"recordings": [inspect(path, encoding) for path in args.cases]}
|
||||
args.output.write_text(json.dumps(receipt, ensure_ascii=False, indent=2) + "\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loaded 100 of 839 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user