* fix(package-verifier): reject overview responses without a procedure count
The analyze and inspect checks compared procedure_count with `< 1`, and an absent
or null normalized_result makes that comparison false, so a binary_overview
response without its required result passed the platform smoke check. Require an
integer procedure count before the positive-count comparison.
* test(package-verifier): cover malformed overview receipts
---------
Co-authored-by: REA Maintenance <76467478+morluto@users.noreply.github.com>
* feat(managed): decode ldstr user strings in member inspection
inspect_managed_members reported every ldstr operand as a bare 0x70 token,
so an analyst could see that a method loads a literal but not which one.
Answering "where is this path or key used" required a second tool.
Read the optional #US stream and add a top-level user_strings inventory:
each distinct token loaded by an inspected method body, once, in token
order, with its #US file offset and decoded UTF-16 value. Instruction
anchors and normalized CIL digests keep their build-local tokens. A missing
#US stream or malformed item is reported against its token as partial
coverage. Evidence recorded before this field reads user_strings as null.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): collect user strings only from ldstr
A malformed ldtoken can carry a 0x70 token, which the operand decoder
classifies as a string operand. Only ldstr loads a literal, so collect
user strings by opcode.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): admit user-string tokens and failures before collecting them
Charge each distinct ldstr token to the decode budget before adding it to
the collection set, and admit each #US failure before retaining it. A
missing #US stream is now one stream-level issue rather than one per
token.
Document why the #US terminal flag is not validated: Mono and Roslyn
assemblies disagree with ECMA-335 II.24.2.4 and the runtime ignores it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(managed): describe missing #US reporting and the unvalidated flag byte
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(managed): cover #US items that run past the stream end
An odd length whose payload crosses the end of #US, and a two-byte
length prefix that starts on the last #US byte, are both rejected as
"User string leaves #US" beside a readable literal. The test fails if
that bounds check is removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): report reserved #US length prefixes as string failures
The compressed-length reader shared by #Blob and #US hard-coded
invalid-blob and "Metadata blob" for a reserved prefix, so a malformed
user string was published as a blob failure. The caller now names the
item it reads: #Blob keeps its code and message, and #US reports
invalid-string.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): append member inspection issues without argument spreading
inspectManagedMembersBytes gathered every producer's issues with
issues.push(...list), passing one argument per issue. A large malformed
input, such as hundreds of thousands of distinct bad ldstr operands or
MemberRef rows, overflows the stack with an untyped RangeError instead of
returning partial coverage. All six sites now use
appendManagedParseIssues, which appends one issue at a time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): classify truncated #US and #Blob length prefixes by item
A two- or four-byte compressed-length prefix cut off by the end of the
artifact went through the generic range check and was reported as
invalid-row. The prefix reader now uses the item's own code for both
reserved and truncated prefixes: invalid-string for #US, invalid-blob for
#Blob. The unused generic range helper is removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): retain user-string terminal flag conformance issues
* test(managed): write ECMA-335 #US terminal flags and cover flag issues
d5b7b0704 reports a #US terminal flag that differs from ECMA-335
II.24.2.4. The fixture heap always wrote 0, so its non-Latin-1 literals
now read as nonconforming. The fixture writes the spec flag, and a new
test flips one flag each way ("ok" set to 1, "it's" set to 0) and checks
that both literals remain beside one issue each.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): classify ldstr operands before collapsing a missing #US
With no #US stream, every collected ldstr operand was counted as a #US
reference and reported only as missing-stream, while the same non-0x70
operand got invalid-heap-index when the stream existed. parseUserStrings
now owns which operands are user-string tokens: any other operand is
invalid-heap-index either way, and only 0x70 tokens count toward the
missing-stream issue. The heap reader drops its duplicate kind check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): state user-string limits in member comparisons
compare_managed_members ignored user_strings, so a method whose ldstr
literal changed at the same token, or legacy Evidence that never examined
literals, could read as unchanged with no caveat. The comparison now adds
an unknown limitation when either side predates user strings and a
not-compared limitation when literals were observed. ExportedType and
user-string inventories share one uncomparedInventory rule.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(managed): advertise ldstr literals in member inspection help
The MCP tool description, the type_names input, and the CLI command and
--type help now say that inspect_managed_members returns the #US string
literals that ldstr loads, and that a type selection limits them to the
literals the selected types' method bodies load.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): read the empty #US entry at offset 0
ECMA-335 II.24.2.4 makes the first #US entry the empty blob: a zero
length with no terminal flag byte. ldstr 0x70000000 loads it, but the
odd-length check rejected it as invalid-string, dropping the literal and
making coverage partial. Offset 0 with length 0 now reads as "".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): reserve #US offset 0 and admit literals before scanning
Offset 0 is the reserved empty #US entry, so a nonempty item there is now
invalid-string instead of a published literal. The literal's projection
is reserved before the terminal-flag scan, so an inadmissible item fails
at the budget without reading every code unit. The scan's loop index no
longer shadows the token's heap index.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): report #US length prefixes wider than ECMA-335 assigns
compressedLength accepted an overlong encoding, such as 0x80 0x05 for a
5-byte item, without comment. It now reports whether the width is the
one ECMA-335 II.23.2 assigns, and the #US reader reports a non-minimal
prefix as an invalid-string conformance issue beside the readable
literal, like the terminal flag. readMetadataUserString returns every
issue it observes. #Blob behavior is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): check the #US length width before the empty entry
The offset-0 empty entry returned "" before the length-width check, so an
overlong empty entry such as 0x80 0x00 read as complete. The width issue
is now collected first and returned with the empty literal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): admit #US width issues only for returned literals
The overlong-prefix issue was reserved before the fatal shape checks, so
an item that then failed, such as 0x80 0x00 away from offset 0, leaked
512 bytes of budget per token. The width issue is now built and admitted
only on paths that return the literal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): keep #US issues out of member metadata status
User-string issues, including terminal-flag and length-prefix
conformance differences, marked metadata.status partial, so
compare_managed_members turned one-sided methods and fields into unknown
even though every member row was examined. #US issues now make coverage
partial while metadata.status describes the member inventory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): give user strings their own coverage
#US issues entered the member coverage, so every consumer that reads it
as member coverage (application graph member facts, comparison status)
reported examined members as partial over a literal conformance
difference. user_strings is now { state, literals, issues }: #US issues
live there and never change the member coverage or metadata.status.
parseUserStrings returns that inventory directly.
compare_managed_members inspects with user strings skipped, since it
never reads literals, so a large literal cannot exhaust the decode budget
of a comparison. Its limitations state once that literals are not
compared, and the ExportedType limitations return to their main form.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): mark user strings partial when a body is not fully decoded
With user strings owning their coverage, an inspected method body that
was partial, malformed, or truncated no longer reached their state, so a
literal after the decode stopped was missing from a "complete" inventory.
parseUserStrings now marks the inventory partial unless every inspected
body was fully decoded, keeping the literals it did observe.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): mark user strings partial when a method row is omitted
A MethodDef row that failed before its body was read was recorded as an
issue and omitted, so its ldstr instructions were never scanned, yet the
user-string inventory could still read as complete. parseUserStrings now
takes the parseMethods result and stays partial whenever a method row
failed, as well as when an inspected body was not fully decoded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): count only method rows that may hide a selected body
Any parseMethods issue marked user strings partial, including an
unreadable name on a method outside the type selection, so a fully
scanned selection read as incomplete. parseMethods now reads each row's
owner first and reports unscannedRows: rows that produced no method while
their owner is selected or unknown. parseUserStrings uses that count.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): treat unreadably named types as possibly selected
Under a type_names selection, a type whose name could not be read may be
a requested type, but its methods were skipped without counting toward
unscannedRows, so an unexamined body left user strings complete. A
method whose owner's name is unreadable now counts as possibly selected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): judge user-string scans by CIL decoding, not body status
body.status is malformed for an invalid exception section even when
every instruction decoded, so user strings read as partial although every
ldstr was examined. The body reader now reports whether its CIL was fully
scanned, independent of exception-region validity, and parseMethods folds
unscanned bodies and unproduced rows into one unscannedMethods count that
parseUserStrings uses instead of reinterpreting body.status.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): stop deduplicating user-string issues
The inventory passed its issues through uniqueManagedParseIssues, which
serializes every issue into an unbudgeted map: about 42 MB of heap and
113 ms for 125,000 issues on Node 24. The issues are unique by
construction (one failure or per-offset conformance issues per distinct
token, one missing-stream issue), so the inventory now returns them, and
its literals, without copies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(managed): preserve selected string coverage unknowns
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
* fix(ghidra): report unapplied PE CodeView PDB limitation (#2259)
When Ghidra analyzes a PE target that references companion debug symbols
in its CodeView (IMAGE_DEBUG_TYPE_CODEVIEW / RSDS or NB10) debug directory,
Ghidra's headless analyzer cannot automatically locate or apply the companion
PDB from isolated runtime directories without companion admission. Procedure
names remain analysis-generated (FUN_<address>).
Parse CodeView debug metadata (RSDS GUID/age and NB10 age) during PE target
resolution, and report an Evidence limitation informing callers and agents
that referenced debug symbols were not applied and procedure names are
analysis-generated.
Fixes#2259
* test(pe): group CodeView header coverage separately
* fix(ghidra): avoid contradictory PDB limitations
* fix(android): make DEX descriptor pattern portable
* fix(ci): isolate Linux Hopper process fixture inventory
* fix(comparison): recheck historical calls-only changes (#2223)
* fix(comparison): preserve incomplete function call evidence
Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.
Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.
* fix(comparison): recheck historical calls-only changes
---------
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
* style: format identity comparison and IDA runner
---------
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
* fix(frida): drain admitted operations and honor queued cancellation
* fix(frida): settle cancelled loads before queued resume
* test(frida): share lifecycle fixtures without duplicating assertions
* fix(frida): preserve operation admission before session close
* test(apple): bound cold archive fixture compilation
* fix(frida): retain cancelled script creation evidence
* test(release): scope preflight schema to selected jobs
* fix(process): report no remaining time after a provider deadline fires
Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).
Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(comparison): recheck historical calls-only changes (#2223)
* fix(comparison): preserve incomplete function call evidence
Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.
Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.
* fix(comparison): recheck historical calls-only changes
---------
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit 688defca0f)
* fix(ci): isolate Linux Hopper process fixture inventory
(cherry picked from commit 8cac51e12184d56a9ba80a4427b3e6082ddac1ec)
* fix(android): make DEX descriptor pattern portable
(cherry picked from commit 1335bcddc569a1cfb34b6efedcb6f2e3aaaa3357)
* fix: repair the static-check failures on the default branch (#2895)
* fix: repair the static-check failures on the default branch
The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.
- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
`src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
`tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
union in `src/application/SupportedClients.ts`, and an unchecked index in
`tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
inferred as `string[][]`.
Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.
Validation: `npm run check:fast` and `npm run format:check` pass.
* fix(setup): keep Devin on the JSON configuration dialect
---------
Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit df3d96da80)
---------
Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com>
Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: OpenAI Codex <codex@openai.com>
perf(javascript): avoid quadratic temporary matching state
Repeated source groups can allocate candidates that later identity matches
discard. Retain the original ambiguous groups until all tiers finish, expand
only unmatched survivors, and append to locally owned key buckets in place.
Constraint: Preserve matching priority, full ambiguity, and public output.
Directive: Do not re-match residual groups that become unique after later tiers.
Tested: 59 relevant tests, npm run check, and test discovery on current main.
Tested: CLI benchmarks and full-suite failure comparison on the recorded base.
Not-tested: Full suite on the updated main; native Linux and Windows workflows.
* feat(native): read embedded cargo-auditable Rust dependency lists
* fix(native): trim PE section padding and compute dependency cycles iteratively
* fix(native): ignore SHT_NOBITS sections and validate dependency edges linearly
* fix(native): bound compiler path scanning and retained hashes
* fix(rust): pass package arrays as named parameterized test cases
* fix(rust): admit graph expansion and preserve resource units
* fix(rust): reject malformed packages, false cycles, and hidden compiler paths
Fail the first malformed cargo-auditable package before Zod collects an
issue per field. Require cyclic dependency indices to match the cycles in
package dependencies. Keep read-only ELF ranges when a section name does
not fit the bounded name window, so a rustc path in that range is still
reported.
* fix(ci): repair integrated checks and Hopper test baseline
* fix(go): verify Mach-O sections against segments
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* fix(ci): skip portable report merge when shards are skipped
The portable consumer can still run after an upstream build failure, even though the test-shard matrix is skipped and no Vitest blob artifacts exist. Guard artifact download and both report merge variants on successful shards; retain the required-shard gate so skipped or failed shards remain failures.
This applies the report-consumer condition from ee0256c740c9be18baae6c56d90285df8069f5b4 to current pinned main without changing action pins or required gates.
* fix(ci): preserve failed shard diagnostics
* docs: clarify current revision CI policy
* fix(native): parse renamed dyld_info re-exports
dyld_info prints a re-export whose name differs from the source library's
symbol as `[re-export] _foo_renamed (_foo from libfoo)`. The export parser
only accepted `(from libfoo)`, so inspect_macho reported a nonexistent
export named `_foo_renamed (_foo from libfoo)` with no source, and the nm
row for `_foo_renamed` survived the name-based merge as a second export
with reexport null.
Accept the optional source symbol, trying the plain `(from lib)` form
first so a library name containing ` from ` keeps its text. The row now
reports `_foo_renamed` re-exported from libfoo and replaces the nm
duplicate. The export schema has no field for the source symbol name, so
exports coverage records a limitation when a renamed re-export is present.
The golden fixture is a dyld_info capture of a synthetic dylib linked with
-Wl,-alias,_foo,_foo_renamed.
* fix(native): split dyld re-export suffix without nested quantifiers
Replace the (?:(.+?)\s+)??from lazy-optional group with a linear string
scan so malformed lines cannot backtrack and a library path containing
" from " still keeps its whole text via the plain-form-first check.
* test(native): cover nested re-export library names
* fix(native): retain reexports missing from nm inventory
---------
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: morluto <17176810+morluto@users.noreply.github.com>
CLI path normalization resolved every nonblank selected string against
the working directory, so executable_path "file:///opt/electron" became
an invented local path and failed with ENOENT for that location instead
of the field's local-path requirement. Leave `file:` and `scheme://`
references unchanged so the shared schema rejects them as non-local
paths. One-letter prefixes stay paths, so Windows drive paths and
relative names containing a colon resolve as before.
Fixes#2577
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Carry the require callee from dependency extraction into lexical scope
validation so optional calls remain visible without admitting shadowed
loaders.
Fixes#2883
* feat(browser): attach passive tools to browsers without HTTP discovery
Accept a literal loopback browser WebSocket,
ws://127.0.0.1:PORT/devtools/browser/ID, as the cdp_endpoint of the six
passive browser tools. Discovery then reads Browser.getVersion and
Target.getTargets over that socket, and the target session adopts the
same socket, so a browser that asks for consent per connection prompts
once per call.
The socket identifier gates access, so Evidence records the endpoint as
ws://127.0.0.1:PORT/devtools/browser/[REDACTED], and runtime
reconciliation accepts that recorded form. HTTP endpoints and their
Evidence are unchanged. Electron, web runtime, scenario and Inspector
endpoints stay HTTP-only.
Refs #2288
* test(browser): match browser-socket captures against HTTP discovery
Check in the real Chrome lane that DevToolsActivePort names the same
browser socket as /json/version, and that inspecting the selected page
through that socket leaves DOM structure and scripts unchanged compared
with an inspection through HTTP discovery.
Refs #2288
* fix(browser): bound browser-socket handshake and report discovery timeouts
Run the browser-socket WebSocket handshake and both discovery commands
under one discovery deadline, so a port that accepts the connection but
never completes the upgrade times out instead of waiting forever.
Report a reached deadline as a timeout without carrying the internal
abort as its cause, so CLI and MCP project provider_timeout rather than
cancelled. Caller cancellation is still reported as cancelled.
Refs #2288
* fix: repair the static-check failures on the default branch
The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.
- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
`src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
`tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
union in `src/application/SupportedClients.ts`, and an unchecked index in
`tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
inferred as `string[][]`.
Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.
Validation: `npm run check:fast` and `npm run format:check` pass.
* fix(setup): keep Devin on the JSON configuration dialect
---------
Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Squash merge of https://github.com/morluto/rea/pull/2756.
Original pull request submitted by @xu91102.
Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
* fix(cli): start on Windows when USERPROFILE is empty
Node's Windows home lookup throws ERR_SYSTEM_ERROR (uv_os_homedir ENOENT)
for an empty USERPROFILE instead of consulting the account profile. Incur
resolves the home while it is imported, so every command, including
`rea --version`, stopped with a raw stack trace before REA ran.
Treat the empty selection as unset in the launcher, before any runtime
import, and cover startup with a compiled-CLI regression.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(process): report no remaining time after a provider deadline fires
Node timers can fire while performance.now() is still a fraction of a
millisecond short of the monotonic deadline. ProviderStartupDeadline then
reported interruption "timeout" and an aborted signal while remainingMs()
returned ceil(0.x) = 1, so polling loops guarded by remainingMs() > 0
(Hopper and Ghidra startup) could run past an elapsed deadline, and the
real-timer test "aborts a bounded wait when its real monotonic deadline
expires" failed intermittently in CI (expected 1 to be 0).
Once the deadline's own timer has classified a timeout, report zero
remaining time. A deterministic regression test fires the timer with the
monotonic clock 0.4 ms short of the deadline.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
ECMA-426 DecodeSourceMapSources reads a content slot only while its index
is within sourcesContent, so a shorter array leaves trailing sources
without content and a longer array adds no sources. Local application
analysis and browser collection rejected any length mismatch and dropped
every original from the map.
The content helper now checks only entry types. Browser collection drops
its separate check because the leaf schema already validates every
supplied entry, and the raw records are still budgeted before parsing.
Fixes#2821
* feat(native): search analyzed memory for masked byte patterns
The diff adds search_bytes to the CLI, the shared MCP contract, Ghidra
inventory, and the headless bridge, and advertises the same operation on
Hopper as unavailable until that provider is verified. Patterns are
hexadecimal byte pairs with optional ?? wildcards and an optional
equal-length bit mask; the TypeScript boundary and the bridge both
require a nonempty pattern, equal lengths, and at least one constrained
bit. Ghidra walks initialized blocks in address order within inclusive
bounds, keeps overlapping matches inside a single block, and returns
each hit with its segment, unique source file offset, and containing
procedure, continuing through a cursor bound to the target, profile,
pattern, mask, and bounds. Each page reports the inclusive ranges
examined and the skipped uninitialized blocks; cancellation returns a
typed error and discards the active Ghidra database.
address_to_file_offset now uses the same unique-offset helper, which
rejects a mapping that is missing, ambiguous, or larger than a safe
integer.
* fix(native): limit unbounded byte search to loaded memory
Without start or end, search_bytes walked every Ghidra block, including
non-loaded file parts such as .symtab and overlay blocks, which the docs
call out of scope. Unbounded searches now skip those blocks and list them
in searched.skipped_not_loaded; explicit bounds in their address space
still search them. Document that a match must end at or before end.
* fix(native): preserve implicit address spaces in range consumers
* fix(native): reject present malformed byte search state
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
* feat(setup): add Kimi Code client support
Registers rea in ~/.kimi-code/mcp.json (or $KIMI_CODE_HOME/mcp.json), preserving unrelated settings with a .rea.backup; registration is command-based stdio with no type field, startupTimeoutMs 30000 and toolTimeoutMs 120000 from MCP_STARTUP_POLICY.
Strict JSON parsing: empty or whitespace-only files initialize; malformed JSON is rejected untouched with no backup; explicit transport other than stdio reports invalid.
Bundled skill uses the shared ~/.agents/skills destination that Kimi Code discovers, installed and verified through the shared-skill flow.
Validated by 19 Kimi-specific tests, 260 adjacent-client regressions passing, check:fast and docs:check, and real-client verification on Kimi Code 0.40.1 (173 tools discovered, real mcp__rea__analyze_javascript_application call succeeded).
* fix(setup): preserve Kimi tool filters and strict JSON coverage
---------
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
* fix(host): separate REA host support from Hopper installer targets
LinuxDistribution.supported carried two meanings at once: that the host runs
REA, and that the automated Hopper installer is tested on it. The doctor read
the first meaning from the second, so Debian-derived hosts such as Kali
reported unsupported_host while their providers worked normally.
Rename the field to hopperSetupSupported, which only the Hopper installer and
setup planning consume, and derive host support from the package family
through supportsReaHost(). Any distribution with a recognized apt, dnf, or
pacman family is now a REA host. The tested Hopper installer list is
unchanged, so Debian and Kali keep falling back to configuring an existing
provider instead of downloading an untested package.
Verified on Kali 2026.3 (Debian testing, x86_64): rea doctor reports a healthy
environment with Ghidra 12.1.4, and analyze_function decompiles an x86_64 ELF
through the MCP server. npm run check, npm run docs:check, and the application
and Ghidra suites pass. No Hopper or IDA host workflow was exercised.
* fix(setup): stop skill checks after first drift
---------
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
* fix(cli): label each uninstall skill root by its client
rea uninstall reported every managed skill destination under the
shared "skill" label (with only Claude Code special-cased), so a
retained or failed directory could not be told apart from the others.
Use the destination's client identifier so the aggregated detail names
each root: "shared: retained (...)", "claude_code: removed (...)",
"antigravity: retained (...)". Retained and failed entries now point to
the specific directory that needs attention.
Fixes#2111
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(cli): align skill summary assertions with client and path labels
---------
Co-authored-by: wjl8636 <wjl8636@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
* fix(comparison): preserve incomplete function call evidence
Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.
Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.
* fix(comparison): recheck historical calls-only changes
---------
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Error results reach the wire as text plus `isError: true`, but the
text-only envelope omitted the flag. An error up to 15 bytes above the
result budget passed both the delivery and transport checks and was sent
as the original diagnostic instead of taking the oversized-error recovery
path.
Rename the error representation to "error-text" and size its envelope
with the flag, so ToolResultDelivery and the error transport share the
complete delivered shape. Successful structured results are unchanged.
Fixes#2557
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(sqlite): keep triggers whose declaration spells the table in another case
SQLite stores a trigger's target in sqlite_schema.tbl_name exactly as
written in CREATE TRIGGER and resolves it by ASCII case folding, while an
index stores the table's own name. The selected_table scope filtered
tbl_name with COLLATE BINARY, so a trigger declared ON users for table
Users was omitted while the schema still reported complete. Match
dependents with COLLATE NOCASE, and report each trigger's table as the
stored table or view name so the selected-table contract still holds.
* fix(sqlite): refuse an inexact table before the case-folded schema scan
With the NOCASE expansion, a selection such as users for table Users
expanded the stored table's declarations before readRows reported the
missing exact name, so large trigger definitions could surface as an
output-limit failure instead of the selection error. Check the exact
table name against PRAGMA table_list first and share the selection
failure with readRows.
* fix(sqlite): fold dependent names only for a row-selectable table
An exact view, virtual or possible shadow table passed the exact-name
guard, so the NOCASE scan could still expand a differently cased trigger
past the reply budget before readRows refused the selection. Fold case
only when the selection is an ordinary table that is not a possible
shadow table, and keep the exact filter otherwise so every refused
selection fails as it did before the case-folding change.
* fix(sqlite): resolve trigger tables without folding unrelated names
The stored-name lookup case-folded every PRAGMA table_list name up front,
so a selected-table inspection of a tiny table paid for large unrelated
identifiers. A selected-table scan only returns the selected table's
dependents, so report the selected name directly; other scopes use an
exact stored name when one matches and fold the stored names once, only
when a trigger spells its target differently.
* perf(sqlite): fold each table name once when matching shadow parents
The extracted shadow-parent helper folded the table name inside the find
callback, once per unresolved virtual table, and the selected table was
checked both before and during the schema scan. Fold the name once per
call as before, and reuse the selected table's result in the scan.
* Single owner for the Chrome DevToolsActivePort startup deadline
Cold Chrome startup tolerance was spelled three ways for one wait: the helper
fell back to 15,000 ms, the runtime fixture lifecycle passed a literal 60,000,
and the real-browser verifier kept a local 60,000 constant. A per-caller literal
quietly changes how long a lane tolerates a cold browser before it fails, and
the failure reported only elapsed_ms, so a reader could not see which ceiling
was hit.
Export BROWSER_DEVTOOLS_STARTUP_TIMEOUT_MS as the owner, use it for the helper
fallback and both call sites, and report startup_timeout_ms in
BrowserStartupError. A source guard keeps callers from restating the number.
* Pin the DevToolsActivePort deadline guard to the startup call argument
---------
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Split two over-long test functions into module-level helpers
(max-lines-per-function): IdaSessionClient transport cases, IDA provider
composition, and function comparison identity cases keep their names and
assertions.
Also fix three typecheck errors: default omitted
direct_invokes_exhaustive to null so the result schema stays JsonValue
compatible, parse the ghidra dossier before spreading it, and delete the
optional beforeCall hook instead of assigning undefined under
exactOptionalPropertyTypes.