mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 13:56:47 -10:00
Compare commits
68
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
800a3f24b7 | ||
|
|
6c895eeb8e | ||
|
|
8f97a45483 | ||
|
|
6fd5d347c7 | ||
|
|
b14b94f0f5 | ||
|
|
7b6d872a25 | ||
|
|
b9a8f8e9f2 | ||
|
|
23e7c4e8e2 | ||
|
|
ac545adae2 | ||
|
|
9593bc536b | ||
|
|
7c389cbefc | ||
|
|
8b74f30bf9 | ||
|
|
7aa943c14b | ||
|
|
33698e6b31 | ||
|
|
fea4f2e397 | ||
|
|
09225b08e1 | ||
|
|
c77735fa83 | ||
|
|
18a8e71c9f | ||
|
|
06d1247836 | ||
|
|
f483eab543 | ||
|
|
df7119aeed | ||
|
|
60bad9ed06 | ||
|
|
3b242491a4 | ||
|
|
e78ac1a55b | ||
|
|
6a226bd092 | ||
|
|
2b8fb387b6 | ||
|
|
0e4b806db1 | ||
|
|
a0a1962e06 | ||
|
|
b0fb51cb4c | ||
|
|
5ae77656e7 | ||
|
|
b594fbc3e7 | ||
|
|
8397a0c9eb | ||
|
|
fbb0e79354 | ||
|
|
c36971953c | ||
|
|
ad7d4c1f4d | ||
|
|
b69d8c5467 | ||
|
|
c01ed783ac | ||
|
|
d3b31a0e29 | ||
|
|
3d5c73f35c | ||
|
|
e556134302 | ||
|
|
aa6f6436c9 | ||
|
|
40b9c03f99 | ||
|
|
baec375c34 | ||
|
|
423d190026 | ||
|
|
6efb384261 | ||
|
|
5fcf0d2dcc | ||
|
|
7bc6c5bf61 | ||
|
|
aa210b7ccc | ||
|
|
ef11f77836 | ||
|
|
0cd7ef456b | ||
|
|
269630f4e1 | ||
|
|
65f1c44734 | ||
|
|
6eb37a7749 | ||
|
|
bcc6775dc1 | ||
|
|
313af7119c | ||
|
|
a95b83c2b0 | ||
|
|
7f90549916 | ||
|
|
b3fd0aa3ee | ||
|
|
b9d1a61dd5 | ||
|
|
79d2fc0540 | ||
|
|
097cd2c988 | ||
|
|
84f62f76e5 | ||
|
|
095a51cf38 | ||
|
|
d47efe1a54 | ||
|
|
cbeeeda97b | ||
|
|
b4118b9683 | ||
|
|
ec3f84b626 | ||
|
|
1201c25542 |
No files matched your search
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "rea",
|
||||
"interface": {
|
||||
"displayName": "REA"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "rea",
|
||||
"source": {
|
||||
"source": "npm",
|
||||
"package": "rea-agents",
|
||||
"version": "6.8.0"
|
||||
},
|
||||
"policy": {
|
||||
"installation": "AVAILABLE",
|
||||
"authentication": "ON_INSTALL"
|
||||
},
|
||||
"category": "Developer Tools"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
name: reverse-engineer-anything
|
||||
description: Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA, and build agent environments (AgentEnv bundles) of web applications from a live instance or a capture. 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: "50"
|
||||
version: "53"
|
||||
---
|
||||
|
||||
# REA
|
||||
@@ -17,6 +17,9 @@ readiness or provider commands.
|
||||
|
||||
Installing this skill supplies instructions; it does not register the REA MCP
|
||||
server, install analysis engines, or add tools to an already-running agent.
|
||||
The exception is a Claude Code or Codex plugin install: the plugin bundles
|
||||
this skill and registers its own `rea` server, outside the state that
|
||||
`rea setup` and `rea doctor` manage.
|
||||
|
||||
If REA tools are available and their registration is not known to be stale,
|
||||
proceed directly to the target. Do not run diagnostics or setup before every
|
||||
@@ -37,42 +40,23 @@ repeat or known not to have started. If it may already have changed the
|
||||
target, or it fails again, tell the user and ask them to restart the MCP
|
||||
client. If REA could not start its connection, retry once, then tell the user
|
||||
and ask them to restart the MCP client. Do not retry a startup failure that
|
||||
returns the same unchanged result. Run `rea doctor` when REA could not start
|
||||
returns the same unchanged result. For plugin installs, follow
|
||||
[plugin recovery](references/connection-and-recovery.md#rea-installed-as-a-client-plugin).
|
||||
For setup-owned registrations, run `rea doctor` when REA could not start
|
||||
or tools stay missing after that restart. For `cleanup_incomplete`, a retryable
|
||||
capacity limit, or `configuration_invalid`, follow the recovery guide's ladder
|
||||
before asking for a restart. Read that guide before a second repair attempt.
|
||||
|
||||
1. Diagnose without changing REA or client configuration. `npx` may download
|
||||
the package and write to npm's cache. For Codex, run
|
||||
`npx -y rea-agents@latest doctor --client codex --json`. Substitute the current
|
||||
supported client: `claude_code`, `claude_desktop`, `codex`, `cursor`,
|
||||
`gemini_cli`, `windsurf`, `devin`, `opencode`, `antigravity`, `copilot_cli`,
|
||||
`commandcode`, `vscode`, `grok_build`, `omp`, or `grok_bot`. `grok_bot` has no local
|
||||
file registration: doctor reports the Grok Bot chat step, and setup does not
|
||||
write `mcp.json`. If the client is unknown, use `doctor --json` and
|
||||
inspect its registration results before choosing a setup scope.
|
||||
2. Distinguish the reason. Missing, malformed, or stale registration needs a
|
||||
scoped configuration repair. An aligned registration with no tools in the
|
||||
active session needs a restart/reconnection; doctor cannot prove that the
|
||||
current agent has connected. A missing provider affects only tasks requiring
|
||||
that provider: static JavaScript inspection needs neither Hopper nor Ghidra,
|
||||
and Android inspection has separate bring-your-own JADX/Java prerequisites.
|
||||
3. For a configuration repair, prepare the read-only plan:
|
||||
`npx -y rea-agents@latest setup --client codex --dry-run --json`.
|
||||
Use the current client's ID, show its exact proposed paths, backups, and
|
||||
changes, and obtain approval before setup writes configuration or installs
|
||||
Hopper. Setup normally installs the matching bundled skill too; include that
|
||||
replacement in the reviewed plan. After approval, apply the same scope with
|
||||
`npx -y rea-agents@latest setup --client codex --yes`. Add `--install-hopper`
|
||||
only if that separate installation was needed and explicitly approved.
|
||||
4. Restart/reconnect the affected agent. Verify that REA tools actually appear
|
||||
in the session, then resume the original investigation. If they remain
|
||||
absent, inspect the client's MCP launch error rather than repeating setup.
|
||||
Choose the installation owner before repairing registration. Plugin installs use
|
||||
[plugin recovery](references/connection-and-recovery.md#rea-installed-as-a-client-plugin);
|
||||
only setup-owned registrations use the guide's
|
||||
[registration repair plan](references/connection-and-recovery.md#repair-registration-when-needed).
|
||||
A plugin-only doctor result of `missing` does not call for `rea setup`.
|
||||
|
||||
REA setup never installs or upgrades Node.js, npm, Homebrew, Java, Ghidra, IDA,
|
||||
JADX, Kuna, Binwalk, or Unblob. Use existing prerequisites; do not install unrelated
|
||||
software to repair MCP registration. For an unsupported client, use manual stdio
|
||||
registration with a version-pinned `rea-agents` package or continue through the CLI.
|
||||
registration with an existing installation’s absolute runtime and entry script, or continue through the CLI.
|
||||
|
||||
A concrete CLI fallback for an operator-supplied JavaScript tree or ASAR is:
|
||||
|
||||
@@ -85,8 +69,11 @@ graph, limitations, and unknowns with the same care as an MCP result; the CLI
|
||||
returns the Evidence record directly. This fallback does not establish that
|
||||
MCP is configured. Native CLI tasks still require their selected engine.
|
||||
Prefer an existing `rea` CLI. When none is installed and REA package acquisition
|
||||
is within the authorized scope, use `npx -y rea-agents@latest` in its place.
|
||||
Configuration repair starts with a read-only setup plan; show paths, backups and
|
||||
is within the authorized scope, use `npx -y rea-agents@latest` in its place from a
|
||||
trusted directory outside the target project, with absolute target paths. Managed
|
||||
setup binds the current installation directly; retain its runtime and package
|
||||
cache entry, or restore that installation before repairing its registration.
|
||||
For setup-owned registration, configuration repair starts with a read-only setup plan; show paths, backups and
|
||||
skill replacement before applying authorized changes. Preserve existing approval
|
||||
and target-execution boundaries.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ does not establish which server an approval wildcard covers. See
|
||||
|
||||
| Observation | Meaning and next step |
|
||||
| --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| No REA tools in the active session | Inspect registration with `rea doctor --client codex --json`. A skill installation alone does not register MCP. |
|
||||
| No REA tools in the active session | If REA was installed as a client plugin, follow the plugin section below. Otherwise inspect registration with `rea doctor --client codex --json`. A skill installation alone does not register MCP. |
|
||||
| Missing, malformed or stale registration | Prepare a scoped setup plan; apply only authorized changes with backups. |
|
||||
| Aligned registration, but no connected tools | Restart/reconnect the client. Doctor checks registration; it cannot establish the current agent's connection. Inspect the client's launch error if reconnection fails. |
|
||||
| MCP connection dropped or REA could not start | If the connection drops, retry that call once only when it is safe to repeat or known not to have started. If it may already have changed the target, or the same failure returns, tell the user REA's connection failed and ask them to restart the MCP client. If REA could not start its connection, retry once, then tell the user and ask them to restart the MCP client. Do not retry a startup failure that returns the same unchanged result. Run `rea doctor` when REA could not start or tools stay missing after restart. Doctor cannot restore a live connection by itself. |
|
||||
@@ -116,6 +116,32 @@ is not a recommendation to change it. Do not invent a setting. Host memory and
|
||||
- `invalid_request`: correct the reported fields from the current schema. Do
|
||||
not repeat the unchanged call.
|
||||
|
||||
## REA installed as a client plugin
|
||||
|
||||
A Claude Code or Codex plugin owns its `rea` server and this skill. Doctor's
|
||||
`--client` and `--skill` checks and `rea setup` read and write only
|
||||
setup-owned state, so for a plugin-only install they report `missing`, and
|
||||
applying setup would add a second `rea` server. If this skill was loaded from
|
||||
a plugin, or the client lists a `rea` plugin, do not prepare a setup plan for
|
||||
missing tools. Instead:
|
||||
|
||||
- Check that the plugin is installed and enabled with the client's own
|
||||
listing: `claude plugin list` and `/mcp` in Claude Code, `codex plugin list`
|
||||
in Codex.
|
||||
- Restart or reconnect the client, then read its launch error for the `rea`
|
||||
server if tools are still absent. The plugin starts its bundled runtime; a missing build or version mismatch
|
||||
requires a reinstall, or a fresh build when developing from source.
|
||||
- Check analysis engines through the connected server's
|
||||
`binary_session.result.tool_availability`. `rea doctor --provider <id> --json`
|
||||
from a shell reports that shell's environment, which can differ from the
|
||||
environment the client gave the plugin server. If you still run it through
|
||||
the package runner, use the version pinned in the plugin manifest's server
|
||||
entry (`rea-agents@<version>`), not `@latest`, so it matches the server the
|
||||
plugin starts.
|
||||
|
||||
Use the setup repair below only when the user wants a setup-owned registration
|
||||
instead of the plugin, and remove or disable the plugin first.
|
||||
|
||||
## Repair registration when needed
|
||||
|
||||
For Codex:
|
||||
@@ -130,7 +156,10 @@ Use the current supported client's ID: `claude_code`, `claude_desktop`, `codex`,
|
||||
`copilot_cli`, `commandcode`, `qwen_code`, `vscode`, `trae`, `grok_build`, `omp`, `pi`,
|
||||
`hermes`, `qoder`, `droid`, `zcode`, `dsh`, `freebuff`, or `grok_bot`. If unknown, inspect `doctor --json` before choosing a
|
||||
scope. `grok_bot` uses a chat registration step rather than local `mcp.json`.
|
||||
For unsupported clients, use version-pinned manual stdio registration or CLI.
|
||||
For unsupported clients, use manual stdio registration with the selected
|
||||
installation’s absolute runtime and entry script, or continue through the CLI.
|
||||
Acquire packages from a trusted directory outside the target project. Keep a
|
||||
package-runner installation and its runtime available while registrations use it.
|
||||
|
||||
Show the plan's exact paths, backups, changes and bundled-skill replacement.
|
||||
Obtain approval for writes when that concrete scope is not already authorized,
|
||||
|
||||
@@ -23,6 +23,14 @@ again if a hint names a gap, then writes the bundle with
|
||||
- Map the application first with `inspect_web_page` (and `capture_web_screenshot`
|
||||
when layout matters): the login form's locators, and the API calls behind the
|
||||
pages that list and edit the entities the user asked about.
|
||||
- `inspect_web_page` cannot sign in. To see pages behind sign-in, run a short
|
||||
`capture_browser_scenario` that signs in and opens them, with
|
||||
`"capture": {"after_each_step": ["accessibility"]}` (`"dom"` for markup).
|
||||
Read a step's tree with `inspect_analysis_view`, `view: {kind:
|
||||
"browser-scenario", collection: "text", step_index, artifact:
|
||||
"accessibility", offset: 0, limit: 2048}`, paging with `offset`. Capture
|
||||
options are only `after_each_step`, `at_end`, `events`, `network` and
|
||||
`limits`.
|
||||
|
||||
## Record a session an environment can learn writes from
|
||||
|
||||
@@ -124,8 +132,10 @@ client with it. Match `wait_for` locators to exactly one element.
|
||||
as a secret reference), submit, then `wait_for` an element only a signed-in
|
||||
user sees.
|
||||
2. Read with `request` steps: each collection the environment needs, twice
|
||||
before any write, so fields that change on their own show up; one item of
|
||||
each.
|
||||
before any write, so fields that change on their own show up; then each
|
||||
object a list shows by id, up to ten per collection. An object known only
|
||||
from a list that leaves out fields its own read shows cannot be read by id
|
||||
or updated in the environment.
|
||||
3. Write the way the application's UI does: fill its forms and click its
|
||||
buttons, so each request carries the fields the UI sends, including ones
|
||||
the server would otherwise derive. Give the click or key press that submits
|
||||
@@ -145,8 +155,17 @@ client with it. Match `wait_for` locators to exactly one element.
|
||||
read 2, list; never several writes before their reads.
|
||||
4. Leave more than a second between a create and its first edit
|
||||
(`wait_for_timeout` with `duration_ms: 1100`).
|
||||
5. Update both objects and read each back; delete both and read each back
|
||||
(expect 404) and the list.
|
||||
5. Update both objects and read each back, changing the fields a person
|
||||
would change (a name, an email address, a status, a due date) rather than
|
||||
only a description: a served update accepts only the members a recorded
|
||||
update stored. Delete both and read each back (expect 404, or the object
|
||||
marked deleted) and the list.
|
||||
6. Vary what the two creates send, however you write them: the first every
|
||||
field the application takes, the second only what a person must give (its
|
||||
title or name), with the other fields left out or empty. A served create
|
||||
requires each member both creates filled in and lets a caller leave out one
|
||||
a create left out or sent empty, so an agent that names only a title is not
|
||||
refused for fields it had no reason to set.
|
||||
|
||||
About `request` steps:
|
||||
|
||||
@@ -226,7 +245,7 @@ withheld with its hint:
|
||||
| `no-call` | Re-record once with a write that succeeds. |
|
||||
| `side-effect` | Stop: the application changes other entities. |
|
||||
| `contradicted` | Stop: a later read disagrees with the modelled effect. |
|
||||
| `body-unusable` | Stop: the body is not a JSON object. |
|
||||
| `body-unusable` | Re-record a UI write once with `request` steps (its body was not exposed); else stop. |
|
||||
|
||||
Use new scratch names on each attempt, and delete what an earlier attempt
|
||||
created before recording again (through the UI, or a `request` step with the
|
||||
|
||||
@@ -652,3 +652,20 @@ page cannot prove absence in unknown coverage. Retained views are historical,
|
||||
not fresh filesystem checks. No decryption, Godot resource decoding, GDScript
|
||||
decompilation or target execution is provided. See
|
||||
[Godot PCK](https://github.com/morluto/rea/blob/main/docs/godot-pck.md).
|
||||
|
||||
UnityFS AssetBundle containers (`.bundle`, `.ab`, YooAsset packages and other
|
||||
byte-identified files) list `CAB-*` SerializedFiles and `.resS` members with
|
||||
header and node facts; none/LZ4/LZ4HC member blocks stream on demand. Bundles
|
||||
declaring asset-bundle encryption keep header facts with unknown directory
|
||||
coverage; LZMA member bytes retain named occurrences with unavailable hashes.
|
||||
Virtual member offsets are not physical carrier locations. Format 6–8 readers
|
||||
cache one decoded block and support retained filtered/paged views. No
|
||||
decryption or SerializedFile object decoding is provided. See
|
||||
[UnityFS](https://github.com/morluto/rea/blob/main/docs/unityfs.md).
|
||||
|
||||
U-Boot legacy uImages use the same inspection/extraction workflows. Prefer a
|
||||
retained summary and selected inventory views for header facts or component
|
||||
IDs. Header addresses/CPU/OS are declarations; payloads remain as stored.
|
||||
Multi-file components retain their aligned physical ranges; the full payload
|
||||
is also materialized. Unknown name text retains its original 32 bytes. See
|
||||
[U-Boot uImage](https://github.com/morluto/rea/blob/main/docs/uimage.md).
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
|
||||
"name": "rea",
|
||||
"description": "Reverse engineer anything with agents, from app behavior down to native binaries.",
|
||||
"owner": {
|
||||
"name": "morluto",
|
||||
"url": "https://github.com/morluto"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "rea",
|
||||
"description": "Reverse engineer anything with agents, from app behavior down to native binaries.",
|
||||
"source": {
|
||||
"source": "npm",
|
||||
"package": "rea-agents",
|
||||
"version": "6.8.0"
|
||||
},
|
||||
"category": "development"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "rea",
|
||||
"version": "6.8.0",
|
||||
"description": "Reverse engineer anything with agents, from app behavior down to native binaries.",
|
||||
"author": {
|
||||
"name": "morluto",
|
||||
"url": "https://github.com/morluto"
|
||||
},
|
||||
"homepage": "https://rea.tools/",
|
||||
"repository": "https://github.com/morluto/rea",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"reverse-engineering",
|
||||
"binary-analysis",
|
||||
"decompiler",
|
||||
"mcp-server"
|
||||
],
|
||||
"skills": "./skills",
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "node",
|
||||
"args": [
|
||||
"${CLAUDE_PLUGIN_ROOT}/scripts/plugin-mcp-launcher.mjs",
|
||||
"6.8.0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"icon": "./assets/rea-plugin.png"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "node",
|
||||
"args": [
|
||||
"./scripts/plugin-mcp-launcher.mjs",
|
||||
"6.8.0"
|
||||
],
|
||||
"cwd": ".",
|
||||
"startup_timeout_sec": 30
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "rea",
|
||||
"version": "6.8.0",
|
||||
"description": "Reverse engineer anything with agents, from app behavior down to native binaries.",
|
||||
"author": {
|
||||
"name": "morluto",
|
||||
"url": "https://github.com/morluto"
|
||||
},
|
||||
"homepage": "https://rea.tools/",
|
||||
"repository": "https://github.com/morluto/rea",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"reverse-engineering",
|
||||
"binary-analysis",
|
||||
"decompiler",
|
||||
"mcp-server"
|
||||
],
|
||||
"skills": "./skills",
|
||||
"mcpServers": "./.codex-plugin/mcp.json",
|
||||
"interface": {
|
||||
"displayName": "REA",
|
||||
"shortDescription": "Reverse engineer anything",
|
||||
"longDescription": "Reverse engineer anything with agents, from app behavior down to native binaries.",
|
||||
"developerName": "morluto",
|
||||
"category": "Developer Tools",
|
||||
"capabilities": [
|
||||
"Interactive",
|
||||
"Read",
|
||||
"Write"
|
||||
],
|
||||
"websiteURL": "https://rea.tools/",
|
||||
"defaultPrompt": [
|
||||
"Use REA to explain how this binary implements a feature.",
|
||||
"Use REA to compare two versions of this application.",
|
||||
"Use REA to recover the sources from this JavaScript bundle."
|
||||
],
|
||||
"composerIcon": "./assets/rea-plugin.png",
|
||||
"logo": "./assets/rea-plugin.png"
|
||||
}
|
||||
}
|
||||
@@ -753,6 +753,11 @@ jobs:
|
||||
- name: Run native Apple and process boundary regressions
|
||||
run: >-
|
||||
npm run test:focused --
|
||||
tests/boundary/providers/hopper/hopperMacLaunchArguments.test.ts
|
||||
tests/acceptance/providers/hopperStartupRecovery.test.ts
|
||||
tests/boundary/providers/hopper/hopperClient.helperFailure.test.ts
|
||||
tests/boundary/providers/hopper/bridgeBootstrapCleanup.test.ts
|
||||
tests/boundary/providers/hopper/bridgeTargetLease.integration.test.ts
|
||||
tests/acceptance/artifactDmgAttachmentRecoveryCli.test.ts
|
||||
tests/boundary/providers/native/nativeDmgArtifactReader.test.ts
|
||||
tests/process-global/dmgCancellationReason.test.ts
|
||||
@@ -901,6 +906,7 @@ jobs:
|
||||
tests/boundary/process/packageNpmInvocation.test.ts
|
||||
tests/boundary/process/packedBridgeVerification.test.ts
|
||||
tests/boundary/process/windowsOutputCapture.test.ts
|
||||
tests/boundary/process/windowsExecutableSelection.test.ts
|
||||
tests/acceptance/webModuleCli.test.ts
|
||||
tests/acceptance/webSourceLocationCli.test.ts
|
||||
tests/boundary/cli/referenceSourceImportStatus.test.ts
|
||||
|
||||
+4
-1
@@ -10,7 +10,10 @@
|
||||
"node_modules/**",
|
||||
"CHANGELOG.md",
|
||||
"package-lock.json",
|
||||
".claude-plugin/plugin.json",
|
||||
".codex-plugin/plugin.json",
|
||||
"_reference/**",
|
||||
"third_party/**"
|
||||
"third_party/**",
|
||||
".codex-plugin/mcp.json"
|
||||
]
|
||||
}
|
||||
+2
-2
@@ -2683,7 +2683,7 @@ Read the [6.1 to 6.2 migration guide](docs/migration-6.2.md) before upgrading. R
|
||||
* enforce conventional pull request titles ([3a21f16](https://github.com/morluto/rea/commit/3a21f16c1832430765083636511a8c12dbccf2dd))
|
||||
* verify installed package with Linux Hopper ([4f97f69](https://github.com/morluto/rea/commit/4f97f699e4d5471557d1ce0ff91e144065d06a61))
|
||||
|
||||
## [0.3.0](https://github.com/morluto/rea/compare/rea-agents-0.2.1...rea-agents-0.3.0) (2026-07-12)
|
||||
## [0.3.0](https://github.com/morluto/rea/compare/rea-0.2.1...rea-agents-0.3.0) (2026-07-12)
|
||||
|
||||
|
||||
### Features
|
||||
@@ -2736,7 +2736,7 @@ Read the [6.1 to 6.2 migration guide](docs/migration-6.2.md) before upgrading. R
|
||||
* redesign and localize README ([051b6db](https://github.com/morluto/rea/commit/051b6db65687fd14826b6462cd623feb2504f123))
|
||||
* redesign and localize README ([910eda6](https://github.com/morluto/rea/commit/910eda6f323ba1151b0771fe5de6c60a91e3d46d))
|
||||
|
||||
## [0.2.0](https://github.com/morluto/rea/compare/rea-0.1.0...rea-0.2.0) (2026-07-12)
|
||||
## [0.2.0](https://github.com/morluto/rea/releases/tag/rea-0.2.0) (2026-07-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
@@ -83,12 +83,15 @@ npx rea-agents setup
|
||||
|
||||
Choose your agents, review the proposed changes, and approve them. Setup adds
|
||||
REA's MCP server and matching workflow instructions, with backups of existing
|
||||
configuration. Restart your agent afterward.
|
||||
configuration. Restart your agent afterward. Setup records the selected runtime and REA
|
||||
entry script directly. If acquired through a package runner, keep its cache entry
|
||||
available. Run package acquisition from a trusted directory outside the target
|
||||
project. Installation and recovery details are in the setup guide below.
|
||||
|
||||
Setup supports Claude Code, Codex, Cursor, Gemini CLI, Grok Build and
|
||||
[other agents](docs/installation.md#supported-agents). See
|
||||
[installation and setup](docs/installation.md) for provider configuration and
|
||||
manual MCP registration.
|
||||
manual MCP registration and Claude Code/Codex plugin installation.
|
||||
|
||||
MCP uses compact input schemas by default to reduce context overhead. Set
|
||||
`REA_MCP_INPUT_SCHEMA_PROFILE=full` in your MCP registration when you need the
|
||||
@@ -425,7 +428,7 @@ Report vulnerabilities through [SECURITY.md](SECURITY.md).
|
||||
|
||||
## Star history
|
||||
|
||||
🎉 **300,000 GitHub stars — thank you!**
|
||||
🎉 **400,000 GitHub stars — thank you!**
|
||||
|
||||
Thanks to everyone using REA, reporting bugs, requesting features, testing builds, and contributing fixes.
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Plugin icon
|
||||
|
||||
`rea-plugin.png` is a square adaptation of the left panel of REA's GitHub
|
||||
social preview, retrieved on 2026-10-28:
|
||||
|
||||
https://repository-images.githubusercontent.com/1209966933/1a3fef9a-be4b-41f4-8830-a94c0e8288cc
|
||||
|
||||
The image was adapted with imagegen, preserving the cyan REA lettering,
|
||||
pixel typography, orange rule, and taglines. Both plugin manifests reference
|
||||
this shared asset; only the PNG is included in the npm package.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -406,7 +406,8 @@ returns that record's recorded response. Selection compares the supplied path
|
||||
parameters against the record's path segments at the parameter indexes, then
|
||||
the supplied query values against the record's query string, and keeps the
|
||||
record with the most matches; ties keep manifest order. A query argument accepts
|
||||
one string or an array of strings for repeated values; the array is matched
|
||||
one string, a number or boolean sent as its JSON text, or an array of strings for
|
||||
repeated values; the array is matched
|
||||
against every occurrence in captured order. The raw recorded query remains the
|
||||
replay evidence. The result reports
|
||||
where the answer came from:
|
||||
@@ -613,8 +614,9 @@ shape and carries no annotations.
|
||||
Path parameters are required strings whose
|
||||
description names the segment index, the templating rule and the observed
|
||||
values (sampled within the list budget above; the manifest keeps them all);
|
||||
query parameter names accept an optional string or array of strings (for
|
||||
repeated values in captured order); write methods accept an
|
||||
query parameter names accept an optional string, number or boolean (a number or
|
||||
boolean is sent as its JSON text) or array of strings (for repeated values in
|
||||
captured order); write methods accept an
|
||||
optional `body` object described by the recorded top-level key types. Each
|
||||
input keeps its own argument: a query name equal to a path parameter's name is
|
||||
advertised as `query_<name>`, a query name longer than 64 characters as
|
||||
@@ -768,7 +770,12 @@ sending a `derived` rule's member to an entity that does not hold the value the
|
||||
recorded call found, a nested clock on an entity holding no such object, and a
|
||||
rendered answer missing a field the recorded response carried, and an update or delete of an entity the state does not hold answers
|
||||
the recorded 400, 404 or 410 of the tool or of an item view of the type, if
|
||||
any. A refused call leaves the state unchanged. Values a write generates are
|
||||
any. A served delete of a type whose item view has a `deleted_flag` (the
|
||||
member, `deleted`, `isDeleted` or `is_deleted`, its reads set `true` on a
|
||||
record the application soft-deleted, as EspoCRM does) keeps the entity until
|
||||
`data/reset` or `data/add`, within the size `data/add` loads, so a read of it by
|
||||
id answers its last fields with that flag `true`. A refused call leaves the
|
||||
state unchanged. Values a write generates are
|
||||
deterministic: the clock starts at `write_clock` and advances one second per
|
||||
served write, sequences take one past both the largest held value and the
|
||||
largest they generated, and UUIDs and tokens derive from the state Evidence
|
||||
|
||||
@@ -239,6 +239,13 @@ serialized size of the report together with its limitations (which repeat
|
||||
archive names) is then checked against an 8 MiB output budget (the exact check
|
||||
is authoritative); an over-budget report is refused, not truncated.
|
||||
|
||||
Signing blocks admit at most 4096 ID/value pairs; at most 4096 signing-relevant
|
||||
META-INF entries are retained. These fixed counts, signer/lineage counts and the
|
||||
report byte budget return nonretryable `resource_constraint` errors naming the
|
||||
effective allowance. This release has no runtime override for them. Waiting for
|
||||
an active scan cannot raise a fixed count, and complete CLI JSON cannot raise the
|
||||
report budget. Occupied scan-queue capacity remains retryable after a scan finishes.
|
||||
|
||||
The whole-file reader allocates the selected snapshot and bounded read scratch;
|
||||
total process memory exceeds the selected file's size. It also computes the complete
|
||||
file SHA-256, so inspection is not header-only. Content digests are recomputed
|
||||
|
||||
@@ -100,12 +100,13 @@ still suppresses all string projection, including in this mode.
|
||||
- **Metadata comes from apktool.yml as apktool printed it.** Version codes
|
||||
keep their own spelling (`'7'` versus `7`); `package_name` is read from
|
||||
the decoded manifest, not inferred.
|
||||
Apktool 3's emitted `version: 3.x.y` header selects its single-line string
|
||||
dialect: literal hashes, single quotes, empty strings, and the text `null`
|
||||
stay part of the value; producer backslashes and Unicode escapes decode
|
||||
once. Older/tagged documents retain YAML scalar semantics; double-quoted
|
||||
values also retain YAML string semantics.
|
||||
The `version: 2.9.0`-or-later header that Apktool 2.9+ and 3 emit selects
|
||||
their single-line string dialect: literal hashes, single quotes, empty
|
||||
strings, and the text `null` stay part of the value; producer backslashes
|
||||
and Unicode escapes decode once. Older/tagged documents retain YAML scalar
|
||||
semantics; double-quoted values also retain YAML string semantics.
|
||||
This distinction follows the released
|
||||
[2.9.0 metadata writer](https://github.com/iBotPeaches/Apktool/blob/b64e257c9b689eb9630ad0241f2fe4f4a749955b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/VersionInfo.java#L38-L42),
|
||||
[3.0.3 metadata writer](https://github.com/iBotPeaches/Apktool/blob/18b5e99cb56ff9451e8aa55b065dcf5bbd616975/brut.apktool/apktool-lib/src/main/java/brut/androlib/meta/VersionInfo.java#L49-L55)
|
||||
and [string escape owner](https://github.com/iBotPeaches/Apktool/blob/18b5e99cb56ff9451e8aa55b065dcf5bbd616975/brut.j.yaml/src/main/java/brut/yaml/YamlStringEscapeUtils.java#L55-L116).
|
||||
- **The manifest is the decoded text**, bounded at 512 KiB and reported as
|
||||
|
||||
@@ -415,7 +415,8 @@ allowance; exhaustion fails the observation explicitly instead of guessing scope
|
||||
Response metadata parses repeated CDP header field lines independently. The
|
||||
Link projection retains their declarations in order. `csp.directives` lists the
|
||||
directives of every CSP policy in order, across field lines and comma-separated
|
||||
policies, without recording policy boundaries, so a directive name can repeat.
|
||||
policies, without recording policy boundaries, so a directive name can repeat
|
||||
across policies. As in CSP3, a repeated name within one policy is ignored.
|
||||
Single-valued policy fields combine their lines as one comma-separated value,
|
||||
as RFC 9110 specifies. Each link keeps its target in `href` and
|
||||
its relationship context in `context_url` with a separate `context_scope`;
|
||||
|
||||
@@ -530,10 +530,11 @@ id the application assigns:
|
||||
that names no string or number fails the step, so later steps never send a
|
||||
missing value, as does a response over 4,000,000 characters: a request step
|
||||
stops reading and aborts its fetch there. A UI step reads a body only when
|
||||
its positive received size is known, at most 12,000,000 bytes, and its
|
||||
Content-Encoding is absent or identity. Compressed responses and unknown or
|
||||
zero cache sizes are refused before materializing the body; decoded text
|
||||
over the character limit also fails. The UI action may already have applied.
|
||||
its decoded size is bounded at 12,000,000 bytes or less: its received size,
|
||||
or the declared Content-Length of one served from cache, times 1032 for each
|
||||
gzip or deflate coding, the most DEFLATE expands. Other codings and unknown
|
||||
or zero sizes are refused before materializing the body; decoded text over
|
||||
the character limit also fails. The UI action may already have applied.
|
||||
- `{saved:name}` sends a saved value in a request step's `path`, including its
|
||||
query, and in a `goto` destination's `url`, percent-encoded; a destination's
|
||||
structured `query` values are sent as declared. A saved value that makes a
|
||||
|
||||
+69
@@ -51,6 +51,15 @@ module page. See
|
||||
[JavaScript application workflows](javascript-application-workflows.md) for
|
||||
views, feature traces and comparisons that take the same saved Evidence.
|
||||
|
||||
Add `--timings` to a command to write one JSON timing record to stderr. For
|
||||
example, `rea inspect-elf-notes program --json --timings > result.json` keeps
|
||||
stdout as a parseable result and writes
|
||||
`{"rea_timings":{"version":1,"command_ms":42}}` to stderr. `command_ms`
|
||||
measures CLI command dispatch and result delivery; it is not provider-only or
|
||||
analysis-only time. The flag is off by default, and the record omits stages
|
||||
that REA did not measure. Root version reporting does not dispatch a command
|
||||
and has no command timing record.
|
||||
|
||||
## JSON Lines record output
|
||||
|
||||
Use `--format jsonl` to consume row-oriented results one record per line:
|
||||
@@ -241,6 +250,17 @@ 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 a UnityFS AssetBundle without running Unity:
|
||||
|
||||
```sh
|
||||
rea inspect-artifact content.bundle --detail summary --evidence_output evidence.json --json
|
||||
rea extract-artifact content.bundle --json
|
||||
```
|
||||
|
||||
Format versions 6 through 8 retain header, block and directory facts with
|
||||
none/LZ4/LZ4HC block decompression. See [UnityFS](unityfs.md) for encryption
|
||||
and LZMA coverage boundaries.
|
||||
|
||||
Inspect a Node.js SEA carrier without running it:
|
||||
|
||||
```sh
|
||||
@@ -251,6 +271,26 @@ rea extract-artifact ./application --json
|
||||
See [Node.js SEA](node-sea.md) for supported layouts, script and asset paths,
|
||||
fuse observations, and the real-fixture verification lane.
|
||||
|
||||
Inventory and extract Intel HEX or S-record regions:
|
||||
|
||||
```sh
|
||||
rea inspect-artifact ./firmware.hex --json
|
||||
rea extract-artifact ./firmware.hex --json
|
||||
```
|
||||
|
||||
See [Intel HEX and S-record images](hex-records.md) for recognition, region
|
||||
names, metadata and limits.
|
||||
|
||||
Inspect and extract stored U-Boot legacy uImage payloads and components:
|
||||
|
||||
```sh
|
||||
rea inspect-artifact ./firmware.bin --detail summary --evidence_output firmware.json --json
|
||||
rea extract-artifact ./firmware.bin --json
|
||||
```
|
||||
|
||||
See [U-Boot uImage](uimage.md) for verified CRCs, physical component ranges,
|
||||
unknown names, resource limits and retained views. Compressed bytes stay stored.
|
||||
|
||||
Inspect local model declarations without loading weights:
|
||||
|
||||
```bash
|
||||
@@ -972,6 +1012,35 @@ values instead of replacing them with an object label. Table widths are measured
|
||||
iteratively, so an admitted row page is not limited by the JavaScript function
|
||||
argument count.
|
||||
|
||||
`--format csv` and `--format tsv` export one selected row collection. Select an
|
||||
exact array path with `--filter-output` and provide comma-separated direct
|
||||
scalar columns with `--columns`; column order is the output column order:
|
||||
|
||||
```bash
|
||||
rea inspect-analysis-view request.json --format csv \
|
||||
--filter-output normalized_result.rows --columns name,address
|
||||
```
|
||||
|
||||
The first line is a REA metadata record,
|
||||
`# rea-delimited-v1 <JSON>`. It carries the selected path, view request, limitations and unknowns,
|
||||
coverage, source coverage and completeness, command, and duration. A metadata
|
||||
value of `null` means that the selected row collection did not provide that
|
||||
fact; it does not mean complete coverage. The following line is the header.
|
||||
CSV records use CRLF line endings; TSV records use LF. Cells containing the
|
||||
delimiter, quotes, or line breaks are quoted and embedded quotes are doubled.
|
||||
Values are emitted as UTF-8 without Unicode normalization. Missing and `null`
|
||||
cells are empty; booleans and finite numbers use their ordinary text forms.
|
||||
String cells and headers that begin with spreadsheet formula syntax are refused
|
||||
with a structured error; JSON retains those values unchanged.
|
||||
|
||||
Delimited output does not flatten nested Evidence or nested field values.
|
||||
It returns a structured error and a nonzero status if the filter does not
|
||||
select exactly one row array, a row is not an object, or a selected cell is
|
||||
nested or formula-leading. JSON remains the complete canonical output; use it
|
||||
to retain fields outside the selected columns or preserve exact formula-leading
|
||||
text. Token counting and token windows are not
|
||||
available for CSV/TSV because slicing would corrupt records or metadata.
|
||||
|
||||
`--filter-output` selects fields from successful command data. Classified failures
|
||||
retain their complete typed diagnostic projection even when the selector matches
|
||||
a field such as `code` or `message`, or names a missing field. `--full-output`
|
||||
|
||||
@@ -14,12 +14,17 @@ interpreter: the npm launcher's `#!/usr/bin/env node` still searches `PATH` for
|
||||
Node. Running that command successfully in a terminal does not establish that
|
||||
the client can launch it.
|
||||
|
||||
For direct JavaScript installations, setup registers the selected runtime
|
||||
For JavaScript installations, setup registers the selected runtime
|
||||
executable and REA script by absolute path. On POSIX, it prefers executable
|
||||
`node` or `bun` and REA aliases on setup's `PATH` only when they resolve to the
|
||||
same selected files. This preserves manager-owned aliases across version updates;
|
||||
otherwise setup uses the selected paths directly. Package-runner registrations keep
|
||||
their selected runner. Setup does not copy your ambient environment into client
|
||||
otherwise setup uses the selected paths directly. This also applies when setup
|
||||
runs through a package runner: retain the selected cache entry and runtime while
|
||||
the registration uses them. Clearing either makes startup fail without resolving
|
||||
another package from the client's project. Restore the same installation before
|
||||
repairing it; an unidentified registration remains untouched. See
|
||||
[installation and recovery](installation.md#package-runners-and-global-installation).
|
||||
Setup does not copy your ambient environment into client
|
||||
settings. To refresh an older registration, preview
|
||||
`rea setup --client <client-id> --skill=false --dry-run`, then run the same command
|
||||
without `--dry-run` and approve the displayed changes. Setup preserves unrelated
|
||||
@@ -497,17 +502,51 @@ comments and unrelated settings. It resolves that file from `HERMES_HOME`, else
|
||||
from the platform default Hermes itself uses — `%LOCALAPPDATA%\hermes` on
|
||||
Windows, `~/.hermes` elsewhere — including any `HERMES_DATA_DIR_SUFFIX`. Hermes
|
||||
connects an entry unless `enabled` is false, so setup states `enabled: true`.
|
||||
Aliases whose anchor is defined in the `rea` entry retain their original resolved
|
||||
values when setup refreshes or uninstall removes that entry. Setup does not
|
||||
redirect another server's alias to REA's new launcher or policy.
|
||||
This also covers an anchor on the removed registration key. References to
|
||||
definitions outside the entry keep sharing the original value, including when
|
||||
another definition later reuses the same anchor name.
|
||||
|
||||
Hermes personal skills install under the active Hermes home's `skills` directory.
|
||||
Setup, doctor and uninstall follow the root's `active_profile` selection, as
|
||||
plain Hermes CLI commands do. An explicit `HERMES_HOME` pointing to
|
||||
`profiles/<name>` selects that named profile directly. An invalid, missing or
|
||||
deleted sticky profile is reported instead of writing into the default home;
|
||||
it also remains an unresolved skill selection, so setup cannot report a
|
||||
successful empty installation. Overrides nested beneath the native Hermes home
|
||||
use that native home's sticky selection and profile root together, matching
|
||||
[Hermes's root rules](https://github.com/NousResearch/hermes-agent/blob/748c79a1c6873124d84071cdabb4062a75913e1f/hermes_constants.py#L83).
|
||||
Repair the profile or run `hermes profile use default` before retrying.
|
||||
Setup, doctor and uninstall share REA's sticky-profile resolver.
|
||||
A custom `HERMES_HOME` outside the platform default home
|
||||
reads its own `active_profile`. An override physically inside the default home
|
||||
uses that default home's sticky selection and profile root together, including
|
||||
when the final override directory does not yet exist.
|
||||
|
||||
For example, with `HERMES_HOME=~/.hermes/sub` and `~/.hermes/active_profile` set
|
||||
to `coder`, setup selects `~/.hermes/profiles/coder/config.yaml` and that
|
||||
profile's skills directory. It does not read `~/.hermes/sub/active_profile`.
|
||||
Without an outer named selection, this override uses `~/.hermes/sub/config.yaml`.
|
||||
Use a home outside the platform default tree for an independent sticky selection.
|
||||
|
||||
This describes REA's current routing, not full Hermes CLI equivalence.
|
||||
At Hermes commit `748c79a1`, the
|
||||
[sticky lookup](https://github.com/NousResearch/hermes-agent/blob/748c79a1c6873124d84071cdabb4062a75913e1f/hermes_cli/main.py#L612-L632)
|
||||
also reads the outer selection, but
|
||||
[`resolve_profile_env`](https://github.com/NousResearch/hermes-agent/blob/748c79a1c6873124d84071cdabb4062a75913e1f/hermes_cli/profiles.py#L2431-L2461)
|
||||
resolves it beneath the configured `HERMES_HOME`. For nested overrides, confirm
|
||||
the profile Hermes actually uses and point `HERMES_HOME` directly at that
|
||||
`profiles/<name>` directory before setup. Compatibility is tracked in
|
||||
[#3939](https://github.com/morluto/rea/issues/3939).
|
||||
|
||||
REA resolves existing ancestor directories before checking physical containment,
|
||||
including symlinks and junctions, even when the override directory does not exist.
|
||||
An override beneath a symlinked default home therefore uses the default home's
|
||||
sticky selection. If `~/.hermes/link` points outside the default home,
|
||||
`HERMES_HOME=~/.hermes/link/new` instead uses that override root, whether or not
|
||||
`new` exists. Previewed destinations retain the configured path spelling.
|
||||
An unreadable path or dangling alias leaves the home unresolved; repair that path
|
||||
before configuring Hermes. Inspect setup's previewed destinations before applying
|
||||
changes.
|
||||
|
||||
An explicit `HERMES_HOME` pointing to `profiles/<name>` selects that named
|
||||
profile directly, bypassing sticky selection. An invalid, missing or deleted
|
||||
sticky profile is reported instead of writing into the default home; repair the
|
||||
profile or run `hermes profile use default` before retrying. It also remains an
|
||||
unresolved skill selection, so setup cannot report a successful empty installation.
|
||||
REA follows `HERMES_HOME` whitespace trimming, environment-variable expansion,
|
||||
and home expansion before resolving that profile. Setup previews the resolved
|
||||
configuration and skill destinations; doctor and uninstall use those same paths.
|
||||
@@ -602,6 +641,12 @@ boundaries. FreeBuff MCP loading and skill discovery have not been verified
|
||||
with an installed native FreeBuff client/version; filesystem tests cover REA's
|
||||
editing, discovery, and ownership behavior only. Automatic applicability
|
||||
requires the FreeBuff profile directory even when the shared MCP file exists.
|
||||
When uninstall cannot establish FreeBuff or another shared-storage reader from
|
||||
product discovery, a malformed or unreadable shared MCP file is retained
|
||||
unchanged and does not stop other REA removals, including `--purge-data`. When
|
||||
FreeBuff or ZCode product applicability is established or unresolved, the same
|
||||
malformed shared file stops uninstall before removals. See
|
||||
[Diagnose, update, and remove](installation.md#diagnose-update-and-remove).
|
||||
|
||||
FreeBuff setup support requires `[email protected]` or later. Older versions
|
||||
reject `rea setup --client freebuff` as an unsupported client. See the
|
||||
@@ -705,10 +750,6 @@ can override an inherited disable. Lower files and other server entries remain
|
||||
unchanged; a legacy server named `servers` that conflicts with the native table
|
||||
requires manual repair before setup can write.
|
||||
|
||||
When Hermes aliases refer to an anchored REA entry, refreshing or removing REA
|
||||
retains the aliases' original resolved values. Setup does not redirect another
|
||||
server's alias to REA's new launcher or policy.
|
||||
|
||||
With no client selected, the interactive skill choice targets the shared
|
||||
`~/.agents/skills` copy. Selecting a client targets its personal destination
|
||||
when supported. Use `rea setup --skill-client shared --skill` for the shared
|
||||
|
||||
@@ -292,3 +292,25 @@ Use [`inspect_devicetree`](devicetree.md) / `rea inspect-devicetree` on an
|
||||
extracted DTB for its node/property tree, reservations, hardware summary and
|
||||
selected phandle relationships. FDT v16/v17 inspection is portable and requires
|
||||
no external tool or disassembler.
|
||||
|
||||
## Inspect Intel HEX and S-record images
|
||||
|
||||
Use `inspect-artifact` / `inspect_artifact` on an Intel HEX or Motorola S-record
|
||||
file for its address regions, declared start address and record facts, then
|
||||
`extract-artifact` / `extract_artifact` for one raw file per region. Decoding is
|
||||
portable and requires no external tool. See
|
||||
[Intel HEX and S-record images](hex-records.md).
|
||||
|
||||
## Inspect U-Boot legacy uImages
|
||||
|
||||
`inspect-artifact` / `extract-artifact` recognize a legacy uImage by its
|
||||
`0x27051956` magic and inventory it without running or decompressing it.
|
||||
The root facts record the 64-byte header (architecture, OS, type, and
|
||||
compression as raw values and names, load and entry addresses, image name,
|
||||
and timestamp) with the header and data CRC32 verified; the stored payload
|
||||
is extractable as `payload.bin`, and multi-file images additionally expose
|
||||
each sub-image as `image-<n>.bin` from the stored size table. Declared
|
||||
addresses are facts from the header, not proven entry points, and compressed
|
||||
payloads stay as stored. Intel HEX/S-record and FIT images are separate
|
||||
readers and are not covered here. See [U-Boot legacy uImage](uimage.md) for
|
||||
component padding, preserved names, resource policies and verification scope.
|
||||
+12
-3
@@ -1,9 +1,18 @@
|
||||
# Godot PCK archives
|
||||
|
||||
`inspect-artifact` and `extract-artifact` read ordinary Godot PCK formats 2, 3
|
||||
`inspect-artifact` and `extract-artifact` read ordinary Godot PCK formats 1, 2, 3
|
||||
and 4 without running the game or requiring Godot. Official Godot 4.4.1, 4.6.1
|
||||
and 4.7.1 produced these respective formats in verification; other engine
|
||||
releases have not been verified. Format 1 is unsupported.
|
||||
and 4.7.1 produced formats 2, 3 and 4 respectively in verification; other engine
|
||||
releases have not been verified. Format 1 compatibility follows the
|
||||
[Godot 3.5 loader](https://github.com/godotengine/godot/blob/3.5/core/io/file_access_pack.cpp#L201-L242)
|
||||
and [exporter](https://github.com/godotengine/godot/blob/3.5/editor/editor_export.cpp#L1094-L1164)
|
||||
layout and is covered by synthetic standalone and embedded fixtures; an actual
|
||||
Godot 3 export has not been exercised.
|
||||
|
||||
Format 1 uses an 84-byte header, no pack or member flags, and absolute member
|
||||
offsets in the complete source file, including an embedded executable carrier.
|
||||
Its reported flags and file base are zero because those fields are absent.
|
||||
Zero-offset removal records remain unsupported; REA does not apply patches.
|
||||
|
||||
Inspection retains the original header and directory facts, including engine
|
||||
version, flags, declared file count, source paths, offsets and declared MD5.
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
# Intel HEX and S-record images
|
||||
|
||||
REA inventories Intel HEX (I8HEX, I16HEX, I32HEX) and Motorola S-record (S19,
|
||||
S28, S37) programming files through `inspect-artifact` / `inspect_artifact`, and
|
||||
materializes their decoded bytes through `extract-artifact` /
|
||||
`extract_artifact`. Decoding is static and needs no external tool.
|
||||
|
||||
```sh
|
||||
rea inspect-artifact ./firmware.hex --json
|
||||
rea inspect-artifact ./firmware.s37 --detail summary --evidence_output evidence.json --json
|
||||
rea extract-artifact ./firmware.hex --json
|
||||
```
|
||||
|
||||
For MCP, open the file with `open_binary`, then call `inspect_artifact`. Summary
|
||||
delivery retains the complete Evidence; use `parent_evidence_id` with
|
||||
`inspect_analysis_view` to read selected regions and fields. `extract_artifact`
|
||||
returns a fresh `output_root` containing one raw file per region.
|
||||
|
||||
## Recognition
|
||||
|
||||
A file is recognized only when its first line, starting at byte 0, is a fully
|
||||
valid record: correct start character, hex digits, byte count, checksum, known
|
||||
record type and fixed length. The file name does not matter. A byte-order mark,
|
||||
a leading blank line or an invalid first record leaves the file unrecognized,
|
||||
so it falls through to plain `file` classification. Recognition never searches
|
||||
for a record later in the file. The container formats are `intel-hex` and
|
||||
`srec`.
|
||||
|
||||
## Regions and metadata
|
||||
|
||||
Each inventory entry is one address-contiguous region of decoded bytes, named
|
||||
`0x<start>-0x<end>.bin` with an inclusive end address of eight lowercase hex
|
||||
digits, for example `0x08000000-0x0800001f.bin`. Regions are listed in ascending
|
||||
address order. Out-of-order records are sorted into regions; gaps separate
|
||||
regions and are never filled.
|
||||
|
||||
Region `format_metadata` (`hex-record-region`) records `start_address`,
|
||||
`end_address`, `length`, `record_count`, and `first_line`/`last_line`: the
|
||||
1-based line span of the contributing data records.
|
||||
|
||||
Root `format_metadata` (`hex-records`) records:
|
||||
|
||||
| Field | Meaning |
|
||||
| ------------------------------------------------- | --------------------------------------------------------------------------------- |
|
||||
| `encoding`, `variant` | `intel-hex` or `srec`; the variant derived from the record types seen, or `mixed` |
|
||||
| `line_count`, `record_count`, `data_record_count` | Lines read, non-blank records, and data records |
|
||||
| `data_bytes`, `duplicate_bytes`, `region_count` | Unique decoded bytes, bytes rewritten with identical values, and regions |
|
||||
| `lowest_address`, `highest_address` | Address bounds of the decoded bytes, or `null` without data |
|
||||
| `termination` | Whether Intel `01` or S-record `S7`/`S8`/`S9` was present |
|
||||
| `start_address`, `start_address_record` | The Intel `05` EIP or the `S7`/`S8`/`S9` address, with its record type |
|
||||
| `start_segment_cs`, `start_segment_ip` | The Intel `03` CS:IP pair |
|
||||
| `header_hex`, `header_text` | S-record `S0` data as hex, and as text only when every byte is printable ASCII |
|
||||
| `declared_data_record_count` | The last `S5`/`S6` count, validated against the data records before it |
|
||||
|
||||
`variant` is `null` for an S-record file in which no data or termination
|
||||
record establishes an address width. Start addresses are declared facts, not
|
||||
proven entry points.
|
||||
|
||||
## Facts, limitations and failures
|
||||
|
||||
| Situation | Outcome |
|
||||
| ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- |
|
||||
| Gaps between regions | Separate regions; never zero-filled |
|
||||
| Out-of-order records | Sorted into regions |
|
||||
| An address written twice with the same value | Accepted and counted in `duplicate_bytes` |
|
||||
| An address written twice with different values | Fails, naming the address and the line spans of both writes |
|
||||
| Checksum mismatch, odd hex length, non-hex character, byte count disagreeing with the line | Fails with the line, declared and computed values, and record type |
|
||||
| Line longer than 521 (Intel) or 514 (S-record) characters, excluding the line ending | Fails before the line is decoded |
|
||||
| Intel type `06` or higher, `S4`, wrong fixed length for Intel `01`–`05` or `S5`–`S9` | Fails |
|
||||
| Missing Intel `01` or `S7`–`S9` | Regions are returned; coverage is unknown and the file may be truncated |
|
||||
| A non-blank record after termination, or a second `S0` | Fails; blank lines are allowed anywhere |
|
||||
| `S5`/`S6` count different from the data records before it | Fails |
|
||||
| Termination type not matching the data address width | Reported as a limitation; the declared fact is kept |
|
||||
| Conflicting repeated `03`/`05` start records | Fails; identical repeats are allowed |
|
||||
| An Intel data record whose 16-bit offset wraps past `0xFFFF` | Fails: producers disagree on whether the address wraps or continues linearly |
|
||||
| I16HEX data above `0xFFFFF`, or data crossing the S1/S2/S3 address width | Fails |
|
||||
|
||||
Lowercase hex, LF and CRLF line endings, and trailing spaces or tabs are
|
||||
accepted. A CR not followed by LF is an invalid character. Failures apply to
|
||||
the whole file and return no partial regions.
|
||||
|
||||
## Limits
|
||||
|
||||
The file must be at most 128 MiB. Every data byte needs two hex digits, so the
|
||||
decoder reserves at most half the file size for decoded bytes before reading,
|
||||
and stores only written addresses. The decoder retains at most 65,536
|
||||
address-discontiguous record runs before merging them into regions. A run is a
|
||||
sequence of consecutive records whose addresses continue each other. Both are
|
||||
fixed resource bounds, not format limits, and fail as limits (`file-size` and
|
||||
`capacity`) with no runtime override. Configurable ceilings are tracked in
|
||||
[#6176](https://github.com/morluto/rea/issues/6176). The line-length limits are
|
||||
the one-byte record count: Intel HEX allows 255 data bytes, while S-record
|
||||
counts include address and checksum bytes.
|
||||
|
||||
Decoded regions are cached only for the reader's lifetime. Each open checks
|
||||
its admitted carrier identity again. Region streams copy at most 64 KiB per
|
||||
chunk, so consumers cannot mutate cached bytes and opening a large region does
|
||||
not copy it in full. Caller cancellation keeps its exact reason, and closing
|
||||
the reader waits for active region streams to close.
|
||||
|
||||
## Analyzing a region
|
||||
|
||||
Inventory does not establish the CPU, byte order or memory map, or that a
|
||||
declared start address is executable. To disassemble a region, extract it and
|
||||
open the region file with `open_binary` using format `raw-binary`, an
|
||||
architecture you choose, and `entry_point` set to the region start address;
|
||||
raw-binary maps file offset 0 at `entry_point`. Each region is opened
|
||||
separately, so references between regions are not resolved. A caller-selected
|
||||
Ghidra language and base address for raw-binary imports is tracked in
|
||||
[#4246](https://github.com/morluto/rea/issues/4246); Thumb-only Cortex-M images
|
||||
may be refused or decoded in ARM state until
|
||||
[#5744](https://github.com/morluto/rea/issues/5744) is resolved.
|
||||
|
||||
## Verification
|
||||
|
||||
Fixtures with computed checksums cover supported record types, checksums,
|
||||
termination, address gaps and overlaps, resource refusals, inventory, extraction
|
||||
and reader lifecycle. CLI and stdio MCP tests exercise the same artifact
|
||||
provider and advertised schemas, including retained Evidence views.
|
||||
|
||||
GNU objcopy 2.40 output was also checked on Linux: the system `true` ELF was
|
||||
converted to Intel HEX and S-record, with additional address-rebased I32HEX
|
||||
and S37 cases. Each yielded 13 regions. A 19,124,817-byte Intel HEX conversion
|
||||
of the system Python 3.11 ELF yielded 11 regions containing 6,799,261 decoded
|
||||
bytes. Public CLI inspection and extraction matched GNU BFD address ranges
|
||||
and independently exported section bytes. Adjacent GNU sections were joined
|
||||
without filling address gaps. These are real producer outputs from system
|
||||
applications, not vendor-firmware or target-execution verification; srec_cat
|
||||
and other producers remain unverified. No general speedup is claimed.
|
||||
@@ -40,7 +40,12 @@ Record the image digest, REA source revision and these dependency versions:
|
||||
- OpenSSL and cURL advertising HTTP3 and HTTP2 in `curl --version`.
|
||||
|
||||
The verified guest used Python 3.14.7, cURL 8.22.0, ngtcp2 1.25.0 and nghttp3
|
||||
1.18.0. Other dependency profiles need verification. Back up the guest's
|
||||
1.18.0. The current requirements update certifi from 2026.7.22 to 2026.10.19
|
||||
to remove the compromised emSign trust root described in
|
||||
[GHSA-6q48-6c7w-9gw2](https://github.com/certifi/python-certifi/security/advisories/GHSA-6q48-6c7w-9gw2).
|
||||
The revised dependency graph has not been rerun in that sealed guest; the
|
||||
historical verification does not cover this update. Other dependency profiles
|
||||
need verification. Back up the guest's
|
||||
`/etc/hosts` before adding `127.0.0.1 lab.test`. Reserve guest TCP/UDP loopback
|
||||
ports 18443 and 19443. These changes belong only to the disposable guest.
|
||||
|
||||
|
||||
+14
-14
@@ -47,20 +47,20 @@ 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), [CPython pyc files](python-pyc.md), [PE certificates](pe-certificates.md), [PE imports](pe-imports.md), [PE TLS/load config](pe-load-config.md), [ELF notes](elf-notes.md), [Linux kernel modules](kernel-modules.md), [Apple IPS](apple-ips.md), [Erlang BEAM](erlang-beam.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
|
||||
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [CPython pyc files](python-pyc.md), [PE certificates](pe-certificates.md), [PE imports](pe-imports.md), [PE TLS/load config](pe-load-config.md), [ELF notes](elf-notes.md), [Apple IPS](apple-ips.md), [Erlang BEAM](erlang-beam.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), [GameCube DOL](ghidra-dol.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), [APK signatures](apk-signature.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), [CPython pyc files](python-pyc.md), [PE certificates](pe-certificates.md), [PE imports](pe-imports.md), [PE TLS/load config](pe-load-config.md), [ELF notes](elf-notes.md), [Linux kernel modules](kernel-modules.md), [Apple IPS](apple-ips.md), [Erlang BEAM](erlang-beam.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
|
||||
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [CPython pyc files](python-pyc.md), [PE certificates](pe-certificates.md), [PE imports](pe-imports.md), [PE TLS/load config](pe-load-config.md), [ELF notes](elf-notes.md), [Apple IPS](apple-ips.md), [Erlang BEAM](erlang-beam.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), [GameCube DOL](ghidra-dol.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), [UnityFS](unityfs.md), [U-Boot uImage](uimage.md), [.NET](managed-code-analysis.md), [Python packages](python-packaged-applications.md), [Android](android-analysis.md), [APK signatures](apk-signature.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), [admission limits and recovery](design/limits-and-recovery.md),
|
||||
|
||||
+170
-61
@@ -3,13 +3,14 @@
|
||||
Run REA, connect your selected agents, and check the tools needed for your task.
|
||||
Setup previews configuration changes and asks for approval before applying them.
|
||||
|
||||
| Task | Section |
|
||||
| --------------------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| Connect an agent | [Start setup](#start-setup), then [review changes](#review-setup-changes) |
|
||||
| Diagnose one task | [Scoped readiness checks](#check-readiness-for-your-task) |
|
||||
| Configure an analysis engine | [Hopper](#hopper), [Ghidra](#ghidra), the [IDA guide](ida-provider.md), or [Kuna](kuna-provider.md) |
|
||||
| Update or remove REA | [Maintenance](#diagnose-update-and-remove) |
|
||||
| Resolve a client-specific problem | [Client configuration](client-configuration.md) |
|
||||
| Task | Section |
|
||||
| ---------------------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| Connect an agent | [Start setup](#start-setup), then [review changes](#review-setup-changes) |
|
||||
| Install a Claude Code/Codex plugin | [Agent plugins and marketplaces](#agent-plugins-and-marketplaces) |
|
||||
| Diagnose one task | [Scoped readiness checks](#check-readiness-for-your-task) |
|
||||
| Configure an analysis engine | [Hopper](#hopper), [Ghidra](#ghidra), the [IDA guide](ida-provider.md), or [Kuna](kuna-provider.md) |
|
||||
| Update or remove REA | [Maintenance](#diagnose-update-and-remove) |
|
||||
| Resolve a client-specific problem | [Client configuration](client-configuration.md) |
|
||||
|
||||
## Start setup
|
||||
|
||||
@@ -169,7 +170,9 @@ Setup leaves existing skill copies in other locations untouched.
|
||||
Setup refuses symbolic links at the selected skill directory or its managed
|
||||
`references` directory, matching uninstall and update's ownership rules. Use a
|
||||
regular skill directory, or select `--skill=false` to configure the client
|
||||
registration without installing the bundled skill.
|
||||
registration without installing the bundled skill. A non-directory ancestor is
|
||||
reported as `ENOTDIR`; preserve the blocking file and correct the directory path
|
||||
before rerunning setup.
|
||||
The final selected directories are checked before approval and again before
|
||||
setup changes any client or Hopper installation. Unselected client destinations
|
||||
do not block other setup choices. Doctor reports linked or non-directory skill
|
||||
@@ -272,7 +275,7 @@ After reviewing an update plan, rerun that same prefixed setup command without
|
||||
`--dry-run` and approve its exact changes. Profile removal likewise needs the
|
||||
prefix on `claude mcp remove --scope user rea` and any `rea uninstall` command;
|
||||
keep the broader client scope of uninstall in mind. Restart the selected
|
||||
profile after changing its pinned registration.
|
||||
profile after changing its registration.
|
||||
|
||||
`claude mcp remove` removes only the registration. To remove REA from this
|
||||
profile without running the broader uninstall flow, first back up the profile's
|
||||
@@ -322,30 +325,26 @@ commands. Record an existing selection first if you intend to restore it after
|
||||
finishing. The configuration path below is the default profile's
|
||||
`~/.claude.json`; a different active profile uses its own config root instead.
|
||||
|
||||
Register with the variable unset:
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
Register with the variable unset, substituting the absolute runtime and entry
|
||||
script from your existing installation or setup dry-run:
|
||||
|
||||
```bash
|
||||
claude mcp add --scope local rea -- npx -y rea-agents@6.8.0 mcp
|
||||
claude mcp add --scope local rea -- "/absolute/path/to/node" "/absolute/path/to/rea/scripts/rea.mjs" mcp
|
||||
```
|
||||
|
||||
On native Windows, use the same project directory and wrap the package-runner
|
||||
shim so the stdio client can launch it:
|
||||
On native Windows, use the same project directory and absolute paths:
|
||||
|
||||
```powershell
|
||||
claude mcp add --scope local rea -- cmd.exe /d /c npx -y rea-agents@6.8.0 mcp
|
||||
claude mcp add --scope local rea -- "C:\Program Files\nodejs\node.exe" "C:\tools\rea\scripts\rea.mjs" mcp
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
Local scope stores the entry under that directory's path in `~/.claude.json`,
|
||||
so other projects don't load it. This route doesn't install the bundled skill.
|
||||
Avoid `--scope project`, which writes `.mcp.json` into the repository where it
|
||||
can be committed or copied. `rea doctor` and `rea uninstall` read only the
|
||||
user-level entry, so they don't report or remove a local-scope registration;
|
||||
remove it with `claude mcp remove --scope local rea` from the same directory.
|
||||
`rea update` doesn't refresh it either, so it stays on the version it names.
|
||||
`rea update` doesn't refresh it either, so it continues using the selected installation.
|
||||
To move it to a newer release, first back up the project entry in
|
||||
`~/.claude.json` and record its environment and other metadata. In particular,
|
||||
preserve caller-selected overrides such as `REA_MCP_INPUT_SCHEMA_PROFILE` and
|
||||
@@ -448,10 +447,15 @@ Lexically equivalent spellings of that default directory, including a trailing
|
||||
separator, a `.` segment, or a repeated separator, share its purge result.
|
||||
The comparison preserves POSIX `..` traversal and literal backslashes.
|
||||
A client configuration that is malformed, unreadable, or at an unsafe
|
||||
path stops the operation before anything is removed, and a client that fails
|
||||
while being updated stops the remaining removals. Uninstall reports each
|
||||
candidate `configuration_path` with its outcome. A client with multiple files
|
||||
keeps their individual `actions` alongside its aggregate status and detail,
|
||||
path stops the operation before anything is removed when that client's
|
||||
product applicability is established or unresolved. Shared registration
|
||||
storage such as `~/.agents/mcp.json` whose product cannot be established
|
||||
(for example when FreeBuff and ZCode discovery directories are absent) is
|
||||
retained unchanged and does not block other REA removals, including cache
|
||||
and state deletion with `--purge-data`. A client that fails while being
|
||||
updated stops the remaining removals. Uninstall reports each candidate
|
||||
`configuration_path` with its outcome. A client with multiple files keeps
|
||||
their individual `actions` alongside its aggregate status and detail,
|
||||
including earlier removals when a later file fails. The paths name
|
||||
the discovered configuration candidates, not resolved physical file identities.
|
||||
For JSON clients, uninstall removes every repeated `rea` member in the effective
|
||||
@@ -547,8 +551,8 @@ non-TTY, and JSON modes without applying configuration changes.
|
||||
## Released package and main
|
||||
|
||||
Repository main documents its current code and generated catalog. `@latest`
|
||||
selects the npm release, and persistent MCP registrations are pinned to the
|
||||
version that performed setup. Installing newer instructions does not update a
|
||||
selects the npm release, and managed MCP registrations bind the installation
|
||||
that performed setup. Installing newer instructions does not update a
|
||||
running server or its registration.
|
||||
|
||||
The npm release checked on **2026-10-11** was **6.4.0**, published from
|
||||
@@ -851,9 +855,13 @@ stage `connection`.
|
||||
If macOS denies Apple Events, allow the application that starts REA under
|
||||
**System Settings → Privacy & Security → Automation**, then retry. REA releases
|
||||
the target reservation when the native helper reports its complete authorization
|
||||
rejection and its arguments cannot alter the helper's generated AppleScript.
|
||||
Arguments containing quotes, backslashes, or line breaks remain unchanged but
|
||||
cannot establish that a failed handoff opened no document. Other failed handoffs
|
||||
rejection after literal argument admission. Before application handoff, REA refuses
|
||||
double quotes, backslashes, or line breaks in the final launcher path or arguments,
|
||||
with `details.launch_rejection.boundary: "argument_encoding"` and the affected source/index.
|
||||
Select literal-safe launcher/runtime paths and loader arguments, or another
|
||||
compatible provider. REA preserves original file names and evidence: a quoted
|
||||
executable name can still work when its prepared image path is literal-safe;
|
||||
database paths must themselves be representable. Other failed handoffs
|
||||
retain ownership until document closure is confirmed. If a timed-out bridge
|
||||
becomes ready later, `close_binary` connects to that run, authenticates its
|
||||
identity, and requests document shutdown. Once its listener is ready, the bridge
|
||||
@@ -1091,14 +1099,15 @@ rea providers --json
|
||||
```
|
||||
|
||||
`rea setup` can configure supported Windows agents and install the bundled
|
||||
REA skill after approval. Direct registrations use Node to launch REA's entry
|
||||
script; package-runner registrations use the pinned `npx` or `bunx` command. Hopper
|
||||
REA skill after approval. Managed registrations use the absolute selected Node or
|
||||
Bun runtime and REA entry script, including when setup ran through a package runner. Hopper
|
||||
installation remains unavailable on Windows. Setup never installs Ghidra,
|
||||
Java, or Python. It preserves valid detected Ghidra/JDK settings in agent
|
||||
registrations. See [Windows Ghidra P0](windows-ghidra-p0.md) for provider diagnostics.
|
||||
|
||||
The separate `rea mcp add` builtin currently uses Incur's package-runner child
|
||||
launcher, which can fail on Windows with `MCP_ADD_FAILED: spawn npx ENOENT`
|
||||
The separate `rea mcp add` builtin delegates most clients to Incur's package-runner
|
||||
child launcher. Run that acquisition from a trusted directory; prefer managed
|
||||
`rea setup` when supported. That child launcher can fail on Windows with `MCP_ADD_FAILED: spawn npx ENOENT`
|
||||
before registering a client. REA's setup path is unaffected. For a client supported
|
||||
by setup, review `rea setup --client opencode --skill=false --dry-run --json`
|
||||
(substitute its client ID), then use the same scoped command without `--dry-run`
|
||||
@@ -1436,6 +1445,21 @@ current setting. For Windows PE resource analysis, the exact name is
|
||||
`WindowsResourceReference`. Disabling an analyzer skips the facts it would have
|
||||
produced.
|
||||
|
||||
Ghidra's Objective-C Message Analyzer can log
|
||||
`Required Objective-C data type not found in data type manager` /
|
||||
`Try adding libobjc.dylib` when a Mach-O links Objective-C runtime symbols but
|
||||
defines no class metadata (thin Cocoa glue in a C++ binary is a common case).
|
||||
That text is non-fatal Ghidra analyzer output: auto-analysis continues, and the
|
||||
analyzer simply skips `objc_msgSend` call-target recovery. REA's headless import
|
||||
does not accept a "add this library" option, and on modern macOS
|
||||
`/usr/lib/libobjc.A.dylib` exists only in the dyld shared cache, so there is no
|
||||
on-disk library to add. Treat the line as a coverage limitation, not an REA
|
||||
failure; check the session's `failure_kind` (and related provider errors) for
|
||||
any real abort. To silence that analyzer, set
|
||||
`REA_GHIDRA_DISABLED_ANALYZERS="Objective-C Message Analyzer"`. For large
|
||||
executables that miss the startup deadline instead, raise
|
||||
`REA_GHIDRA_STARTUP_TIMEOUT_MS` and the [Ghidra heap](#ghidra-heap-and-cpu-controls).
|
||||
|
||||
### Ghidra heap and CPU controls
|
||||
|
||||
Set resource controls in the environment that launches the CLI or MCP server,
|
||||
@@ -1537,6 +1561,87 @@ read-only operations, digest identity, and runtime cleanup. The separate
|
||||
ordinary user. It requires the built Windows native bundle and an existing
|
||||
Ghidra/JDK installation; neither is inferred from startup alone.
|
||||
|
||||
## Agent plugins and marketplaces
|
||||
|
||||
REA's npm package is the shared Claude Code and Codex plugin artifact. It
|
||||
contains `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, and the
|
||||
canonical generated `skills/` tree. Both manifests select `./skills` and run a plain Node bootstrap pinned to
|
||||
that exact package version. The client must have a supported standalone `node`
|
||||
on its PATH. Claude expands its documented `CLAUDE_PLUGIN_ROOT` in the script
|
||||
argument; Codex resolves `cwd: "."` from the installed plugin root before
|
||||
launching the relative script. The bootstrap verifies the package version and runs its bundled runtime in
|
||||
the same process, preserving stdio and REA's provider/worker cleanup. It does
|
||||
not start another package runner or download a different runtime. The existing release skill
|
||||
generator also pins CLI recovery commands to the same version.
|
||||
|
||||
The repository's single-plugin marketplaces are declared in
|
||||
`.claude-plugin/marketplace.json` and `.agents/plugins/marketplace.json`.
|
||||
Both select an exact `rea-agents` npm version instead of mutable source-checkout
|
||||
contents. Release Please updates their selectors alongside the plugin manifests.
|
||||
No plugin-only publication or generated skill files committed to Git are needed.
|
||||
The qualified release archive contains a runtime-only manifest and
|
||||
`npm-shrinkwrap.json` plus bundled runtime dependencies, including PTY packages
|
||||
for each supported OS and CPU; development dependencies and development-only overrides
|
||||
stay in the source manifest and lockfile. Both clients receive this same archive.
|
||||
|
||||
This distribution becomes installable after the first qualified npm release
|
||||
containing these plugin manifests. Earlier published packages are not changed
|
||||
by this source proposal. For local source development, run `npm ci && npm run build:cached` to generate
|
||||
the runtime and packaged skill before loading the repository as a plugin.
|
||||
A source plugin uses the locally built runtime and generated skill; rebuild after
|
||||
source changes. Missing builds and mismatched version pins fail with an actionable
|
||||
error. The authored `.agents/skills/` tree is not the plugin's shipped skill layout.
|
||||
Windows source workflows that need native controls still require the
|
||||
[Windows source-checkout build](https://github.com/morluto/rea/blob/main/native/windows/README.md#build).
|
||||
|
||||
In Claude Code:
|
||||
|
||||
```text
|
||||
/plugin marketplace add morluto/rea
|
||||
/plugin install rea@rea
|
||||
```
|
||||
|
||||
In Codex CLI, add the marketplace and install REA:
|
||||
|
||||
```bash
|
||||
codex plugin marketplace add morluto/rea
|
||||
codex plugin add rea@rea
|
||||
```
|
||||
|
||||
Each release updates both manifest versions and their pinned server version
|
||||
together, which is when installed copies refresh the bundled skill and move to
|
||||
the new server. A plugin does not run `rea setup`, so provider
|
||||
configuration still follows the sections above. Use the plugin or a
|
||||
`rea setup` registration for a client, not both, to avoid two `rea` servers.
|
||||
|
||||
`rea doctor` does not see plugin-owned state. Its client and skill checks read
|
||||
only the registrations and skill locations that `rea setup` manages, so for a
|
||||
plugin-only install `rea doctor --client <id>` and `rea doctor --skill` report
|
||||
`missing` and recommend `rea setup`. Do not follow that remediation. Check the
|
||||
plugin with the client instead (`claude plugin list` and `/mcp` in Claude
|
||||
Code, `codex plugin list` in Codex).
|
||||
|
||||
For analysis engines, ask the connected server: have the agent call
|
||||
`binary_session` and read `result.tool_availability`, which reports what the
|
||||
server the plugin started can use, with reasons and remediation. See
|
||||
[MCP contracts](mcp-contracts.md).
|
||||
|
||||
A plugin does not put `rea` on your `PATH`. To check an engine from a
|
||||
terminal, use the package runner at the version the plugin pins:
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
```bash
|
||||
npx -y rea-agents@6.8.0 doctor --provider ghidra --json
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
If your installed plugin is older, use the version in its manifest instead.
|
||||
That report describes the terminal's environment, not the plugin server's.
|
||||
The server inherits the client's environment, so a setting such as
|
||||
`GHIDRA_INSTALL_DIR` that exists only in your terminal does not reach it.
|
||||
|
||||
## MCP Registry
|
||||
|
||||
REA is published in the official MCP Registry as `io.github.morluto/rea`. Registry
|
||||
@@ -1547,40 +1652,45 @@ For a client that supports Registry discovery, search for `io.github.morluto/rea
|
||||
and select the npm package. The published metadata launches the existing stdio
|
||||
server through `npx` with the `mcp` command.
|
||||
|
||||
For a client that requires manual configuration, use:
|
||||
For supported clients, prefer `rea setup`: it records the absolute JavaScript
|
||||
runtime and entry script of the REA installation performing setup. Client starts
|
||||
then use that installation directly, keeping the client's working directory
|
||||
without running npm or Bun package resolution there. Registry clients control
|
||||
their own package acquisition; Registry metadata cannot name local absolute paths.
|
||||
|
||||
<!-- x-release-please-start-version -->
|
||||
For manual stdio configuration, substitute the absolute paths of an existing
|
||||
installation (use the command shown in a setup dry-run):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "rea-agents@6.8.0", "mcp"]
|
||||
"command": "/absolute/path/to/node",
|
||||
"args": ["/absolute/path/to/rea/scripts/rea.mjs", "mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
Run current setup to migrate recognized older `npx`/`bunx` registrations, with
|
||||
backups, then restart the client. Unrelated or unidentified registrations remain
|
||||
untouched. Setup does not prewarm a separate package-runner command.
|
||||
|
||||
Persistent registrations should use one exact package version. `rea setup`
|
||||
writes the same package-runner shape, or `bunx rea-agents@<version> mcp` when
|
||||
setup runs through `bunx`, and pins it to the exact version that performed
|
||||
setup. Run current setup to refresh an older registration, then restart the
|
||||
client.
|
||||
|
||||
Package runners key their package cache by the requested spec, so the entry
|
||||
installed for the `@latest` invocation that ran setup cannot serve the pinned
|
||||
registration. Setup therefore materializes the pinned entry once, best-effort
|
||||
with a bounded timeout, before finishing. When that step cannot run, for
|
||||
example offline, the first client start performs the one-time install inside
|
||||
the client's MCP startup deadline and may need one more restart.
|
||||
If setup itself runs from a package-runner cache, the registration depends on
|
||||
that cache entry and the selected runtime remaining installed. Removing either
|
||||
makes startup fail; it does not download or resolve another package. Restore the
|
||||
same installation and rerun setup. Changing package requests, cache roots, or
|
||||
runtimes can create a different installation identity. If the old entry cannot be
|
||||
identified, restore it first or manually remove the confirmed REA registration
|
||||
before running setup again. A global installation provides a more durable path.
|
||||
|
||||
## Package runners and global installation
|
||||
|
||||
A package-runner invocation can use a REA version installed in the current project. To
|
||||
request the latest release explicitly, use:
|
||||
Package runners can select project-local packages and read project configuration,
|
||||
even for explicit version requests. Acquire REA from a trusted working directory,
|
||||
outside the target project, and pass absolute target paths to analysis commands.
|
||||
REA cannot validate package acquisition that happens before its entrypoint starts.
|
||||
To request the latest release explicitly, use:
|
||||
|
||||
```bash
|
||||
npx rea-agents@latest setup
|
||||
@@ -1597,16 +1707,14 @@ For an intentional rollback, make the package request explicit:
|
||||
npm exec --yes --package=rea-agents@2.4.0 -- rea setup
|
||||
```
|
||||
|
||||
Setup continues to pin persistent MCP registrations to the exact version that
|
||||
performed setup. Running current setup later migrates unversioned or older
|
||||
managed registrations through the normal reviewed setup transaction.
|
||||
Setup binds persistent MCP registrations to the installation that performed setup.
|
||||
Running current setup later migrates recognized legacy package-runner registrations
|
||||
through the normal reviewed setup transaction.
|
||||
|
||||
With Bun, `bunx rea-agents setup` (or `bunx rea-agents@latest setup`) is the
|
||||
equivalent entry point. Setup run through `npx` registers
|
||||
`npx -y rea-agents@<version> mcp`; setup run through `bunx` or `bun x` registers
|
||||
`bunx rea-agents@<version> mcp`. On Windows, setup wraps either command in
|
||||
`cmd.exe /d /c` so clients can start the runner's command shim. Neither
|
||||
registration points into the runner's temporary package cache.
|
||||
equivalent acquisition entry point. Managed registrations use the selected Bun
|
||||
executable and REA entry script directly. Windows registrations likewise use
|
||||
absolute runtime and script paths, without a package-runner command shim.
|
||||
|
||||
REA supports Node.js 22.x (>=22.23.4), 24.x (>=24.22.1), and 26.12.1+. Node.js 23, 25, earlier 26.x releases, and prereleases are unsupported. It uses the npm already paired with that runtime and never upgrades Node.js, npm, or Homebrew.
|
||||
|
||||
@@ -1640,8 +1748,9 @@ Review the plan, approve the selected client changes, then restart those clients
|
||||
This registers the installed entrypoint directly; installing the global package
|
||||
alone does not replace existing `npx` registrations. Each client still has its own
|
||||
REA process and analysis state. Direct registrations follow the installed CLI;
|
||||
use `rea update` to update it. Running setup through `npx` again selects the pinned
|
||||
package-runner command. Do not register paths inside npm's temporary package cache.
|
||||
use `rea update` to update it. Running setup through `npx` again selects the
|
||||
installation that executes setup, potentially in npm's temporary package cache;
|
||||
retain that cache entry for as long as the registration uses it.
|
||||
|
||||
### npm workspace errors before REA starts
|
||||
|
||||
|
||||
@@ -156,6 +156,10 @@ Select one literal seed kind: node ID, route, string, API, IPC channel, module,
|
||||
or native export. Matching is exact or literal substring matching; regular
|
||||
expressions and executable predicates are not accepted. The trace includes all
|
||||
matching seeds and the complete graph reachable in the selected direction.
|
||||
`both` follows relationships in either direction, except that a container
|
||||
reached through `contains` from one of its children only leads further up its
|
||||
containment chain, so a shared asset or artifact root does not pull in its
|
||||
other children.
|
||||
|
||||
The result contains the matching basis, complete reachable subgraph, terminal
|
||||
paths, authority summaries, and native handoffs. A handoff binds
|
||||
|
||||
@@ -364,6 +364,13 @@ Selected bounded text is then parsed as inert data to recover:
|
||||
relative to the calling module; string worker and service-worker paths use
|
||||
the document base URL or Node's working directory, which static analysis
|
||||
does not establish, so they keep their expression and stay unresolved);
|
||||
- Vite preload dependency tables (`__vite__mapDeps`): `./` entries resolve
|
||||
relative to the importing chunk; base-relative entries such as
|
||||
`assets/chunk.js` carry a `vite-output:` specifier and resolve under the one
|
||||
ancestor directory of the importer that holds them, the build output
|
||||
directory. A file found there but not analyzed, such as a stylesheet, stays
|
||||
`unavailable` with its path. Selecting only the assets directory leaves them
|
||||
`not-found`;
|
||||
- route, network endpoint, storage, and vendor-marker observations;
|
||||
- local source-map declarations and original
|
||||
source names and content digests;
|
||||
|
||||
@@ -301,6 +301,15 @@ unknown metadata. Missing omission counts remain null. Summary output carries
|
||||
every matching record; actual transport admission still applies. Omitted
|
||||
`detail` preserves complete canonical bundle delivery; filters require summary.
|
||||
|
||||
Residual-unknown mutations reject missing Evidence or unknown references and
|
||||
unproven verified resolutions with nonretryable `invalid_request` diagnostics.
|
||||
`details.constraint` carries the validator's `kind`, owning `unknown_id`, and
|
||||
missing `evidence_id` or `related_unknown_id` where applicable. Correct the
|
||||
reported input or supply the required retained Evidence; unchanged retries do
|
||||
not repair these constraints. Rejected mutations leave retained state unchanged.
|
||||
Revision conflicts remain retryable `revision_conflict`; broken revision histories
|
||||
remain `evidence_integrity_mismatch`.
|
||||
|
||||
`list_unknowns` returns every matching current head by default. For a smaller
|
||||
inline response, supply `offset` and `limit`, then follow `next_offset` until it
|
||||
is null. `total` counts all matching heads; each page preserves complete records
|
||||
|
||||
@@ -4,6 +4,20 @@ REA exposes six provider-neutral investigation workflows through the MCP
|
||||
`prompts` capability. They complement the generated tool catalog; they do not add, remove, or
|
||||
invoke tools by themselves.
|
||||
|
||||
Clients that expose tools but not prompts can use the target-free
|
||||
`guided_workflows` tool with `action: "list"` or `action: "render"`. CLI callers
|
||||
can use `rea guided-workflows` to list workflows, or pass `--action render`,
|
||||
`--workflow-id ID`, and `--arguments-json '{"feature":"offline search"}'` to
|
||||
render one. Both paths use the same contracts and renderer as native prompts;
|
||||
rendering returns guidance only and does not inspect a target, start analysis,
|
||||
or change session state.
|
||||
|
||||
The list contains workflow identifiers, descriptions, and argument requirements;
|
||||
render returns only the selected workflow's text. Unknown argument names and
|
||||
non-string values are rejected with field paths. `action: "list"` accepts no
|
||||
workflow identifier or arguments. The MCP adapter respects the same
|
||||
`REA_MCP_TOOL_FAMILIES` scope as native prompts, including render requests.
|
||||
|
||||
## Prompt inventory
|
||||
|
||||
| Prompt | Purpose |
|
||||
|
||||
+29
-3
@@ -224,7 +224,30 @@ child output. Child output remains in the capture Evidence on stdout.
|
||||
resource limits, and normalization settings have defaults; see
|
||||
`processScenarioSchema` for the exact contract. Event times are milliseconds
|
||||
from launch, must be ordered, and must fall within `timeout_ms`. Inputs marked
|
||||
`sensitive` are sent to the process but persisted as a byte-count placeholder.
|
||||
`sensitive` are sent to the process but persisted with empty `data`,
|
||||
`sensitive: true`, and `input_bytes`. Exact nonempty CR/LF-separated input
|
||||
literals are removed from retained terminal text, including cross-chunk and
|
||||
VT-styled echoes. Rendering uses that redacted stream, so intermediate terminal
|
||||
states and serialized state cannot restore removed bytes. Normalized text is
|
||||
checked again. Encoded, transformed, or arbitrarily edited values are not a
|
||||
secret-detection contract; their privacy remains unknown. Unmarked inputs and
|
||||
caller-selected environment values remain evidence.
|
||||
|
||||
The capture-local matcher admits at most 65,536 prefix states and 8,388,608
|
||||
construction work units. Projection uses at most 32 MiB of reusable position
|
||||
indexes and 67,108,864 work units across one capture. Capacity failures return a
|
||||
non-retryable resource error and withhold terminal text in partial observations.
|
||||
If output admission leaves gaps, the sensitive capture withholds retained
|
||||
terminal text rather than interpreting separated fragments as a complete stream.
|
||||
`raw_terminal.redacted_bytes` reports bytes removed from admitted original
|
||||
chunks; these bytes still consume `output_bytes` alongside retained safe bytes.
|
||||
Private display effects remain unknown, with frame order and provenance retained.
|
||||
|
||||
This changes the sensitive-input Evidence representation: consumers of the old
|
||||
`<redacted-input:N-bytes>` placeholder must read `input_bytes` instead. Existing
|
||||
saved captures remain readable and are not rewritten; old files may still
|
||||
contain previously echoed input. A private-input comparison policy version
|
||||
separates old and new captures; reacquire both sides for comparable observations.
|
||||
Environment overrides are recorded in the scenario commitment; inherited
|
||||
values are not copied into Evidence.
|
||||
|
||||
@@ -234,7 +257,9 @@ A bare executable name uses the scenario's `PATH` override, then the inherited
|
||||
An explicitly empty `PATH` searches only the working directory. Executable,
|
||||
working-directory, and observation paths preserve native `symlink/..` traversal.
|
||||
|
||||
Terminal dimensions must satisfy `columns × (rows + scrollback) ≤ 1,000,000`.
|
||||
Terminal width must be at least two columns: the pinned xterm renderer cannot
|
||||
represent a one-column screen. This applies to the initial size and every resize.
|
||||
Terminal dimensions must also satisfy `columns × (rows + scrollback) ≤ 1,000,000`.
|
||||
Every scheduled resize uses the same selected scrollback and must fit this
|
||||
working-buffer budget. REA rejects oversized scenarios before launching the
|
||||
command; it never clamps the requested dimensions. This limit is independent
|
||||
@@ -294,7 +319,8 @@ own classification until its exact resources are released.
|
||||
|
||||
Terminal frame `data` is the comparison text after the selected normalization.
|
||||
New captures also preserve `raw_data` when normalization changes a PTY chunk;
|
||||
otherwise `data` is already the original text. Older captures may lack the
|
||||
otherwise `data` is already that text. For sensitive captures, both fields
|
||||
contain only the safe projection; no unredacted backup is retained. Older captures may lack the
|
||||
original text for changed chunks. Both values participate in Evidence identity,
|
||||
while comparisons and trace assertions use the normalized text. Retaining the
|
||||
original adds at most `limits.output_bytes` of text, because it comes from the
|
||||
|
||||
@@ -350,7 +350,7 @@ npx rea-agents@latest setup
|
||||
|
||||
## سجل النجوم
|
||||
|
||||
🎉 **300,000 نجمة على GitHub — شكرًا لكم!**
|
||||
🎉 **400,000 نجمة على GitHub — شكرًا لكم!**
|
||||
|
||||
شكرًا لكل من يستخدم REA، ويبلّغ عن الأخطاء، ويقترح ميزات، ويختبر إصدارات البناء، ويساهم بالإصلاحات.
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ Melde Sicherheitslücken gemäß [SECURITY.md](../../SECURITY.md).
|
||||
|
||||
## Sternverlauf
|
||||
|
||||
🎉 **300.000 GitHub-Sterne – vielen Dank!**
|
||||
🎉 **400.000 GitHub-Sterne – vielen Dank!**
|
||||
|
||||
Danke an alle, die REA nutzen, Fehler melden, Funktionswünsche äußern, Builds testen und Fehlerbehebungen beitragen.
|
||||
|
||||
|
||||
@@ -352,7 +352,7 @@ npx rea-agents@latest setup
|
||||
|
||||
## Ιστορικό αστεριών
|
||||
|
||||
🎉 **300.000 αστέρια στο GitHub — σας ευχαριστούμε!**
|
||||
🎉 **400.000 αστέρια στο GitHub — σας ευχαριστούμε!**
|
||||
|
||||
Ευχαριστούμε όλους όσοι χρησιμοποιούν το REA, αναφέρουν σφάλματα, ζητούν νέες λειτουργίες, δοκιμάζουν builds και συνεισφέρουν διορθώσεις.
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ Informa de vulnerabilidades siguiendo [SECURITY.md](../../SECURITY.md).
|
||||
|
||||
## Historial de estrellas
|
||||
|
||||
🎉 **300.000 estrellas en GitHub: ¡gracias!**
|
||||
🎉 **400.000 estrellas en GitHub: ¡gracias!**
|
||||
|
||||
Gracias a quienes usan REA, informan de errores, solicitan funciones, prueban compilaciones y contribuyen con correcciones.
|
||||
|
||||
|
||||
@@ -333,7 +333,7 @@ npx rea-agents@latest setup
|
||||
|
||||
## تاریخچهٔ ستارهها
|
||||
|
||||
🎉 **۳۰۰٬۰۰۰ ستارهٔ GitHub — سپاسگزاریم!**
|
||||
🎉 **۴۰۰٬۰۰۰ ستارهٔ GitHub — سپاسگزاریم!**
|
||||
|
||||
از همهٔ کسانی که از REA استفاده میکنند، خطاها را گزارش میدهند، قابلیتهای جدید پیشنهاد میکنند، buildها را آزمایش میکنند و در بهبود پروژه مشارکت دارند سپاسگزاریم.
|
||||
|
||||
|
||||
@@ -345,7 +345,7 @@ Signalez les vulnérabilités via [SECURITY.md](../../SECURITY.md).
|
||||
|
||||
## Historique des étoiles
|
||||
|
||||
🎉 **300 000 étoiles GitHub — merci !**
|
||||
🎉 **400 000 étoiles GitHub — merci !**
|
||||
|
||||
Merci à toutes les personnes qui utilisent REA, signalent des bugs, proposent des fonctionnalités, testent les builds et contribuent des correctifs.
|
||||
|
||||
|
||||
@@ -366,7 +366,7 @@ Laporkan kerentanan melalui [SECURITY.md](../../SECURITY.md).
|
||||
|
||||
## Riwayat Bintang
|
||||
|
||||
🎉 **300.000 bintang GitHub, terima kasih!**
|
||||
🎉 **400.000 bintang GitHub, terima kasih!**
|
||||
|
||||
Terima kasih kepada semua orang yang menggunakan REA, melaporkan bug, meminta fitur, menguji build, dan berkontribusi dalam perbaikan.
|
||||
|
||||
|
||||
@@ -345,7 +345,7 @@ Segnala vulnerabilità tramite [SECURITY.md](../../SECURITY.md).
|
||||
|
||||
## Cronologia delle stelle
|
||||
|
||||
🎉 **300.000 stelle su GitHub — grazie!**
|
||||
🎉 **400.000 stelle su GitHub — grazie!**
|
||||
|
||||
Grazie a tutti coloro che usano REA, segnalano bug, propongono funzionalità, testano le build e contribuiscono con correzioni.
|
||||
|
||||
|
||||
@@ -350,7 +350,7 @@ npx rea-agents@latest setup
|
||||
|
||||
## スター履歴
|
||||
|
||||
🎉 **GitHub スター 300,000 件、ありがとうございます!**
|
||||
🎉 **GitHub スター 400,000 件、ありがとうございます!**
|
||||
|
||||
REA の利用、バグ報告、機能の要望、ビルドのテスト、修正への貢献に感謝します。
|
||||
|
||||
|
||||
@@ -350,7 +350,7 @@ npx rea-agents@latest setup
|
||||
|
||||
## 스타 기록
|
||||
|
||||
🎉 **GitHub 스타 300,000개, 감사합니다!**
|
||||
🎉 **GitHub 스타 400,000개, 감사합니다!**
|
||||
|
||||
REA를 사용하고, 버그를 보고하고, 기능을 요청하고, 빌드를 테스트하고, 수정에 기여해 주신 모든 분께 감사드립니다.
|
||||
|
||||
|
||||
@@ -355,7 +355,7 @@ npx rea-agents@latest setup
|
||||
|
||||
## Star ചരിത്രം
|
||||
|
||||
🎉 **250,000 GitHub stars — നന്ദി!**
|
||||
🎉 **400,000 GitHub stars — നന്ദി!**
|
||||
|
||||
REA ഉപയോഗിക്കുന്നവർക്കും, bugs റിപ്പോർട്ട് ചെയ്യുന്നവർക്കും, features ആവശ്യപ്പെടുന്നവർക്കും, builds പരിശോധിക്കുന്നവർക്കും, fixes സംഭാവന ചെയ്യുന്നവർക്കും നന്ദി.
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ Zgłaszaj podatności zgodnie z [SECURITY.md](../../SECURITY.md).
|
||||
|
||||
## Historia gwiazdek
|
||||
|
||||
🎉 **300 000 gwiazdek na GitHubie — dziękujemy!**
|
||||
🎉 **400 000 gwiazdek na GitHubie — dziękujemy!**
|
||||
|
||||
Dziękujemy wszystkim, którzy używają REA, zgłaszają błędy, proponują nowe funkcje, testują kompilacje i przesyłają poprawki.
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ Relate vulnerabilidades conforme [SECURITY.md](../../SECURITY.md).
|
||||
|
||||
## Histórico de estrelas
|
||||
|
||||
🎉 **300.000 estrelas no GitHub — muito obrigado!**
|
||||
🎉 **400.000 estrelas no GitHub — muito obrigado!**
|
||||
|
||||
Obrigado a todos que usam o REA, relatam bugs, sugerem funcionalidades, testam builds e contribuem com correções.
|
||||
|
||||
|
||||
@@ -429,7 +429,7 @@ Raportează vulnerabilitățile conform [SECURITY.md](../../SECURITY.md).
|
||||
|
||||
## Istoricul stelelor
|
||||
|
||||
🎉 **300.000 de stele pe GitHub — vă mulțumim!**
|
||||
🎉 **400.000 de stele pe GitHub — vă mulțumim!**
|
||||
|
||||
Mulțumim tuturor celor care folosesc REA, raportează erori, propun funcționalități,
|
||||
testează versiuni și contribuie cu remedieri.
|
||||
|
||||
@@ -348,7 +348,7 @@ npx rea-agents@latest setup
|
||||
|
||||
## История звёзд
|
||||
|
||||
🎉 **300 000 звёзд на GitHub — спасибо!**
|
||||
🎉 **400 000 звёзд на GitHub — спасибо!**
|
||||
|
||||
Спасибо всем, кто использует REA, сообщает об ошибках, предлагает новые функции, тестирует сборки и помогает с исправлениями.
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ npx rea-agents@latest setup
|
||||
|
||||
## ประวัติดาว
|
||||
|
||||
🎉 **300,000 ดาวบน GitHub — ขอบคุณทุกคน!**
|
||||
🎉 **400,000 ดาวบน GitHub — ขอบคุณทุกคน!**
|
||||
|
||||
ขอบคุณทุกคนที่ใช้ REA รายงานบั๊ก เสนอฟีเจอร์ ทดสอบบิลด์ และช่วยแก้ไข
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ Güvenlik açıklarını [SECURITY.md](../../SECURITY.md) üzerinden bildirin.
|
||||
|
||||
## Yıldız geçmişi
|
||||
|
||||
🎉 **GitHub'da 300.000 yıldız — teşekkürler!**
|
||||
🎉 **GitHub'da 400.000 yıldız — teşekkürler!**
|
||||
|
||||
REA'yı kullanan, hata bildiren, özellik talep eden, derlemeleri test eden ve düzeltmelere katkıda bulunan herkese teşekkürler.
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ npx rea-agents@latest setup
|
||||
|
||||
## Історія зірок
|
||||
|
||||
🎉 **300 000 зірок на GitHub — дякуємо!**
|
||||
🎉 **400 000 зірок на GitHub — дякуємо!**
|
||||
|
||||
Дякуємо всім, хто використовує REA, повідомляє про помилки, пропонує нові функції, тестує збірки й допомагає з виправленнями.
|
||||
|
||||
|
||||
@@ -432,7 +432,7 @@ Security vulnerabilities [SECURITY.md](../../SECURITY.md) ke zariye report karei
|
||||
|
||||
## Stars ki history
|
||||
|
||||
🎉 **300,000 GitHub stars — shukriya!**
|
||||
🎉 **400,000 GitHub stars — shukriya!**
|
||||
|
||||
REA use karne, bugs report karne, features suggest karne, builds test karne aur
|
||||
fixes contribute karne wale sab logon ka shukriya.
|
||||
|
||||
@@ -348,7 +348,7 @@ Báo cáo lỗ hổng theo [SECURITY.md](../../SECURITY.md).
|
||||
|
||||
## Lịch sử sao
|
||||
|
||||
🎉 **300.000 sao trên GitHub — xin cảm ơn!**
|
||||
🎉 **400.000 sao trên GitHub — xin cảm ơn!**
|
||||
|
||||
Cảm ơn mọi người đã sử dụng REA, báo lỗi, đề xuất tính năng, kiểm thử bản dựng và đóng góp bản sửa lỗi.
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ npx rea-agents@latest setup
|
||||
|
||||
## Star 歷史
|
||||
|
||||
🎉 **GitHub Star 達到 300,000 個,感謝大家!**
|
||||
🎉 **GitHub Star 達到 400,000 個,感謝大家!**
|
||||
|
||||
感謝每一位使用 REA、報告錯誤、提出功能需求、測試建置和貢獻修復的朋友。
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ npx rea-agents@latest setup
|
||||
|
||||
## Star 历史
|
||||
|
||||
🎉 **GitHub Star 达到 300,000 个,感谢大家!**
|
||||
🎉 **GitHub Star 达到 400,000 个,感谢大家!**
|
||||
|
||||
感谢每一位使用 REA、报告错误、提出功能需求、测试构建和贡献修复的朋友。
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,112 +0,0 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"repository": "morluto/rea",
|
||||
"version": "6.7.0",
|
||||
"base": "2c4338db1fdb7773e05376301c558cff2a0b4b80",
|
||||
"target": "b83b9caf4a5f0241a5fd370792f1aed1bffbb2cf",
|
||||
"commits": [
|
||||
{
|
||||
"sha": "8cbaf6c7021b97dd4de6b5771c35fbed06b240e9",
|
||||
"subject": "fix(tests): isolate command finalization module mocks",
|
||||
"body": "fix(tests): isolate command finalization module mocks\n\nMove the five registered WASM finalization cases into the existing per-file isolated project. Preserve all result, exit-code, observation and cleanup assertions. A preloaded shared CLI graph reproduces three failures before the move; the isolated covered packet passes.\n\nFixes #4959\n",
|
||||
"parents": ["2c4338db1fdb7773e05376301c558cff2a0b4b80"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "morluto",
|
||||
"email": "[email protected]",
|
||||
"user": {
|
||||
"login": "morluto",
|
||||
"__typename": "User"
|
||||
}
|
||||
}
|
||||
],
|
||||
"pullRequests": []
|
||||
},
|
||||
{
|
||||
"sha": "6b180783ed04ebc87548c8385c80e05725804c47",
|
||||
"subject": "fix(tests): budget CLI coverage from parsed options",
|
||||
"body": "fix(tests): budget CLI coverage from parsed options\n\nUse the pinned Vitest parser for coverage and shard flags. Keep ordinary CLI workers at 384 MiB and covered workers at the tested 512 MiB with unchanged concurrency. Retain complete coverage and add actual-config regressions for false, provider-only and spaced-shard options.\n\nValidation: 27 focused cases; 16 covered cases with CLI preloaded; 5 docs checks; typecheck/lint; exact discovery of 1578 files. Measured covered control: 22.18s elapsed, 46.52s CPU, about 2.33 GiB sampled summed process-family RSS on macOS arm64. Full Linux CI pending. No runtime/device verification claimed.\n\nFixes #4960\n",
|
||||
"parents": ["8cbaf6c7021b97dd4de6b5771c35fbed06b240e9"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "morluto",
|
||||
"email": "[email protected]",
|
||||
"user": {
|
||||
"login": "morluto",
|
||||
"__typename": "User"
|
||||
}
|
||||
}
|
||||
],
|
||||
"pullRequests": []
|
||||
},
|
||||
{
|
||||
"sha": "bb212ffecb47e129cc569b9867b2b6e7c9b65685",
|
||||
"subject": "fix(tests): inherit server identity deadline policy",
|
||||
"body": "fix(tests): inherit server identity deadline policy\n\nRemove per-case ten-second overrides from MCP catalog and identity checks. Keep the suite-owned bounded ordinary and coverage deadlines, all assertions, and session cleanup.\n\nFixes #4967\n",
|
||||
"parents": ["6b180783ed04ebc87548c8385c80e05725804c47"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "morluto",
|
||||
"email": "[email protected]",
|
||||
"user": {
|
||||
"login": "morluto",
|
||||
"__typename": "User"
|
||||
}
|
||||
}
|
||||
],
|
||||
"pullRequests": []
|
||||
},
|
||||
{
|
||||
"sha": "b83b9caf4a5f0241a5fd370792f1aed1bffbb2cf",
|
||||
"subject": "fix(setup): edit the duplicate JSON member that clients read (#4957)",
|
||||
"body": "fix(setup): edit the duplicate JSON member that clients read (#4957)\n\nJSON.parse, JSONC parse, and native Claude Code keep the last duplicate\nobject member, and parseClientConfiguration therefore validates the last\none. The shared setup/uninstall serializer instead edited text through\njsonc-parser's modify and findNodeAtLocation, which select the first\nmatching member, so setup and uninstall on configurations with repeated\nserver tables or rea members wrote into a shadowed member and failed\nreadback.\n\nLocate every JSON text edit by the effective (last) member in one place,\nreplacing the first-member modify path. Removing an effective member\nthat shadows an earlier duplicate would activate that duplicate, whatever\nits ownership, so the serializer now returns a tagged shadowed_member\nfailure and setup and uninstall refuse before writing a backup or the\nconfiguration.\n\nFixes #4952\n\nCo-authored-by: Claude Opus 5.5 <[email protected]>",
|
||||
"parents": ["bb212ffecb47e129cc569b9867b2b6e7c9b65685"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Kaoru0822",
|
||||
"email": "[email protected]",
|
||||
"user": {
|
||||
"login": "kaoru0822-kitauji",
|
||||
"__typename": "User"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Claude Opus 5.5",
|
||||
"email": "[email protected]",
|
||||
"user": {
|
||||
"login": "claude",
|
||||
"__typename": "User"
|
||||
}
|
||||
}
|
||||
],
|
||||
"pullRequests": [4957]
|
||||
}
|
||||
],
|
||||
"pullRequests": [
|
||||
{
|
||||
"number": 4957,
|
||||
"title": "fix(setup): edit the duplicate JSON member that clients read",
|
||||
"url": "https://github.com/morluto/rea/pull/4957",
|
||||
"author": {
|
||||
"login": "kaoru0822-kitauji",
|
||||
"__typename": "User"
|
||||
},
|
||||
"mergeCommit": {
|
||||
"oid": "b83b9caf4a5f0241a5fd370792f1aed1bffbb2cf"
|
||||
},
|
||||
"issues": [
|
||||
{
|
||||
"number": 4952,
|
||||
"url": "https://github.com/morluto/rea/issues/4952",
|
||||
"author": {
|
||||
"login": "xu91102",
|
||||
"__typename": "User"
|
||||
},
|
||||
"repository": {
|
||||
"nameWithOwner": "morluto/rea"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
-1
@@ -349,7 +349,10 @@ tag remains in main's ancestry. Keep the released tag unchanged.
|
||||
|
||||
The synchronization must include `.release-please-manifest.json`,
|
||||
`package.json`, both root versions in `package-lock.json`, `CHANGELOG.md`,
|
||||
`server.json`, and the versioned documentation examples. Run
|
||||
`server.json`, both plugin manifests (`.claude-plugin/plugin.json` and
|
||||
`.codex-plugin/plugin.json`), the Codex server declaration (`.codex-plugin/mcp.json`), both
|
||||
marketplaces (`.claude-plugin/marketplace.json` and
|
||||
`.agents/plugins/marketplace.json`), and the versioned documentation examples. Run
|
||||
`npm run docs:generate`, `npm run docs:check`, and the release configuration
|
||||
tests against the combined tree. After the merge, verify that the released
|
||||
tag is an ancestor of main:
|
||||
@@ -383,3 +386,18 @@ tag. Do not dispatch a fresh publish phase to repair an already-created
|
||||
release: Release Please will not create the same release again. Do not move
|
||||
the tag, delete the release, or unpublish npm as a retry. A defective public
|
||||
package requires a reviewed correction and a new version.
|
||||
|
||||
### Plugin artifact metadata
|
||||
|
||||
Package verification selects files with npm, then packs an owned staging directory.
|
||||
The release manifest omits development dependencies and development-only overrides;
|
||||
its generated `npm-shrinkwrap.json` preserves the runtime dependency resolutions
|
||||
and integrity values from `package-lock.json`. Runtime or unresolved overrides
|
||||
fail packaging rather than being dropped. Source metadata is never rewritten.
|
||||
The archive bundles production dependencies because Codex fetches npm plugins
|
||||
without installing dependencies. The producer includes all locked PTY platform
|
||||
archives after checking their integrity, without re-resolving the runtime tree.
|
||||
Optional Frida stays outside the bundle so ordinary npm installation can retain
|
||||
its host-specific installer. This also lets Claude install locked dependencies under its plugin policy while
|
||||
retaining development security overrides. Both plugin marketplaces select the
|
||||
same qualified npm artifact, which the release workflow publishes unchanged.
|
||||
@@ -0,0 +1,13 @@
|
||||
# Implementation plan
|
||||
|
||||
1. **Query values.** Widen the query property schema and spell numbers and booleans as JSON text where a call enters
|
||||
serving.
|
||||
2. **Sequence start.** `start` on sequence rules and their schema; the exemplar records integer fields above every held
|
||||
value; serving and replay floor the next value at `start`.
|
||||
3. **Later rules.** Merging a later create adopts its `composed`, `template` or started `sequence` rule when it spells
|
||||
the exemplar's value.
|
||||
4. **Made objects.** The `made-object` rule and schema; its explanation, merge, later-call check, counts taken, serving
|
||||
and own members.
|
||||
5. **Bulk link.** `respondedIds` reads the object of a one-element array member; `anyLinkedSource` lets a linked
|
||||
create take any path values its plan stores in fields, in replay and serving.
|
||||
6. **Skill, tests and docs.**
|
||||
@@ -0,0 +1,46 @@
|
||||
# Serve the calls agents make and the values applications count
|
||||
|
||||
Issue: https://github.com/morluto/rea/issues/4933
|
||||
|
||||
## Outcome
|
||||
|
||||
Eval agents' trajectories on rebuilt environments, and the write plans of the recordings they ran on, showed three
|
||||
gaps REA could close:
|
||||
|
||||
- **Query values.** Served tools typed each query parameter as a string or an array of strings, while agents send page
|
||||
sizes and flags as JSON numbers and booleans (`maxSize: 50`, `withItems: true`). The MCP server refused those calls
|
||||
before REA saw them: 63 of 418 stored eval runs hit it, in EspoCRM, Homebox and PhotoPrism, and 18 never got the tool
|
||||
to answer.
|
||||
- **Counted and made values.** Vikunja numbers a project's tasks past those deleted earlier, so a created task's
|
||||
`index` is not one past what the state holds; its `identifier` spells that index; and each created task gets a
|
||||
`subscription` object the application makes with it. A second create disagreed with the first on all three, so
|
||||
task creates were withheld.
|
||||
- **Bulk creates.** Vikunja's UI creates a task through `POST /projects/{id}/tasks/bulk`, sending and answering the
|
||||
task wrapped in `tasks: [ … ]`. The answer's id sat inside the array, so REA linked the call to no type.
|
||||
|
||||
The diagnosis also found that a recorded update stores only what it sent (EspoCRM's updates never changed an email
|
||||
address), and that an object known only from a list that leaves out fields its own read shows can be neither read by
|
||||
id nor updated. Both are recording gaps the skill now addresses.
|
||||
|
||||
## Behaviour
|
||||
|
||||
- **Query values.** A query parameter accepts a number or boolean besides a string or an array of strings, and serving
|
||||
sends it as its JSON text.
|
||||
- **Sequences from a start.** A field the first create showed above every value its type held, which a later create
|
||||
shows one past the first's, is a `sequence` with that `start`: serving never goes below it.
|
||||
- **Rules that spell the first create.** A field the plan kept as shown takes a later create's rule when that rule also
|
||||
spells the first create's value: a `composed` or `template` string, or the `sequence` above.
|
||||
- **Made objects.** A created object no other entity of the type holds otherwise, with a member the request stamped, is
|
||||
a `made-object`: stamped members take the write's clock, members holding the entity's identity take its id, counts
|
||||
go one past the largest value held objects show from a `start`, and other members stay as recorded.
|
||||
- **Linked bulk creates.** A create's answer names the id of the one object a one-element array member holds. A linked
|
||||
create accepts any path values when it creates into its type's own collection, whose path has no parameters, and
|
||||
stores each path value in a field, as a Vikunja task stores its project: no path value changes where it creates.
|
||||
- **Skill.** Recordings read each listed object by id, up to ten per collection, before writing.
|
||||
|
||||
Plans and views that need none of this are unchanged.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- No write tool of the 130 recorded captures goes from served to withheld.
|
||||
- Rebuilt from the same recordings, the branch passes at least the write tasks #7274 passes.
|
||||
@@ -0,0 +1,13 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Accept numbers and booleans for query parameters.
|
||||
- [x] Start a sequence from the value its first create took.
|
||||
- [x] Adopt a later create's rule that spells the first create's value.
|
||||
- [x] Learn, check and serve objects made with an entity.
|
||||
- [x] Link creates whose answer wraps the entity in a one-element array, and let a linked create take path values
|
||||
it stores.
|
||||
- [x] Ask recordings to read each listed object by id.
|
||||
- [x] Add view, serving, MCP boundary and unit tests.
|
||||
- [x] Document the rules and the query argument types.
|
||||
- [x] Measure the recorded captures and served write tasks.
|
||||
- [x] Run static, formatting, documentation and test gates.
|
||||
@@ -0,0 +1,88 @@
|
||||
# Verification
|
||||
|
||||
Executed on macOS with Node v24.22.1 on this branch merged with #7274 at `45832406` (main `8706068b`), and on Ubuntu
|
||||
24.04 VMs for the task runs.
|
||||
|
||||
## Diagnosis
|
||||
|
||||
The 418 eval runs stored on the comparison VMs, with their agent trajectories:
|
||||
|
||||
- 63 runs had a call refused before REA saw it because a query value came as a JSON number or boolean (`maxSize`,
|
||||
`pageSize`, `withItems`, `isLocation`, PhotoPrism's `count`, `offset`, `merged`, `public`), and in 18 the agent never
|
||||
got that tool to answer.
|
||||
- EspoCRM's contact update fails in every run: the served update accepts only the members recorded updates stored
|
||||
(`description`, `name`, `lastName`), and a seeded contact known only from lists can be neither read by id nor
|
||||
answered for. On #7274, gpt-oss passes the task by creating a replacement contact and deleting the original.
|
||||
- Vikunja's task creates were withheld by an unexplained `index`, `identifier` and `subscription` (main's recording),
|
||||
or had no state target, being bulk creates (four recordings).
|
||||
|
||||
## Gates
|
||||
|
||||
- `npm run -s check:fast`, `format:check`, `scan:todos`, `docs:check`, `verify:test-discovery`, `mcp-catalog:check` and
|
||||
`jscpd`: passed. `npx knip` reports `staticFileUrlPath` and `ObjcClassImportBindingCoverage`, which main `8706068b`
|
||||
added and this branch does not touch.
|
||||
- `npx vitest run --project domain --project services`: 11,430 passed, 6 skipped. `--project adapters`: 7,437 passed, 19
|
||||
skipped, 3 failed: the Inspector cancellation test and two AppKit asset-catalog tests, which fail the same way on
|
||||
#7274 at `e29e9c74`.
|
||||
- `npm run -s test:focused --` the environment build, export and serve CLI boundaries, the analysis-view CLI and MCP
|
||||
boundaries and the environment serve MCP boundary: passed. `outputSchemaProfile`, `toolSchemaValidity`,
|
||||
`mcpSchemaValidation` and `outputSchemaSemanticConstraints`: passed; the compact catalog stays under its frame.
|
||||
|
||||
## Tests
|
||||
|
||||
- View: two task creates at a project path learn `index` as a `sequence` from 15, `identifier` as a `template` around
|
||||
it and `subscription` as a `made-object` whose `id` counts from 21 and whose `entity_id` holds the task's own id; one
|
||||
create alone keeps `index` as recorded; a bulk create whose body and answer wrap the task in `tasks` links to the
|
||||
tasks type and is served; an object with no member the request stamped makes no rule.
|
||||
- Serving: created tasks take index 15 then 16, `#15` then `#16`, and a subscription numbered 22 holding the second
|
||||
task's id; a bulk create answers in its envelope, and one into another project stores that project.
|
||||
- Query values: a number or boolean query value is spelled as its JSON text, and a list paged by a number answers as
|
||||
with the same text; through the MCP server, `maxSize: 2` and `withItems: true` replay their recording exactly. The
|
||||
served card of the `renamed` fixture grows 637 bytes, 91 for each of its seven query parameters.
|
||||
- The seven behaviour tests fail on #7274 at `4ede1305`.
|
||||
|
||||
## Write plans and views over recorded captures
|
||||
|
||||
The 130 captures #7274 measured, built by #7274 at `45832406` and by this branch: 615 plans, 332 served and 333.
|
||||
|
||||
- No write goes from served to withheld. Newly served: Vikunja's bulk task create in #7260's final recording.
|
||||
- Vikunja's task create in main's recording is now explained, by its counter, template and made subscription, but
|
||||
main's `unknown-clock` withholds it in both builds, as that capture has no clock anchor. Measured on #7274 at
|
||||
`4ede1305`, before that rule, it was served too (339 to 341).
|
||||
- Six plans that stay withheld explain more: Homebox entity creates learn `assetId` as a counter from 5 in four
|
||||
recordings, and Vikunja task creates in two.
|
||||
- In #7260's two Vikunja recordings whose UI writes lost their bodies, the bulk create now counts as a task write, so
|
||||
the scratch task it made is no longer held as seeded (35 to 34 and 36 to 35 initial entities), a read tool that read
|
||||
only that task replays its recordings, and later reads check fewer lines. Every other view is unchanged.
|
||||
|
||||
## The same recordings, built by #7274 and by this branch
|
||||
|
||||
Claude Code (`claude-sonnet-5-5`) recordings from earlier rounds, rebuilt by #7274 at `e839af3f` (its last two merges
|
||||
change no census) and by this branch, their write tasks run with `fireworks_ai/gpt-oss-120b` then
|
||||
`fireworks_ai/glm-5p3-flash`, each checked from the environment's state:
|
||||
|
||||
| Recording | Write tasks | #7274 | This branch |
|
||||
| ---------------------------------------------------------- | ----------- | ----------- | ----------- |
|
||||
| Vikunja: #7260 final (bulk creates) | 3 + 2 | 1/5 1/5 | 2/5 2/5 |
|
||||
| Vikunja: main (two), saved-values (two), #7260 (first two) | 6 × (3 + 2) | 6/30 6/30 | 6/30 6/30 |
|
||||
| EspoCRM: main, #7260 (three), saved-values | 5 × 3 | 15/15 10/15 | 15/15 10/15 |
|
||||
| Homebox: main, #7260 final, saved-values (two) | 4 × 2 | 6/8 6/8 | 6/8 6/8 |
|
||||
| Total | 58 | 28/58 23/58 | 29/58 24/58 |
|
||||
|
||||
No recording passes fewer tasks with this branch.
|
||||
|
||||
- **Vikunja, bulk creates:** the create task passes with both models. The served bulk create numbers the task and makes
|
||||
its subscription, and creates into Lighthouse Rota though the recording created into the inbox.
|
||||
- **Refused calls:** agents' calls were refused at input validation 34 times with #7274 (30 in Vikunja) and once with
|
||||
this branch, and agents made 286 tool calls against 317.
|
||||
- **Main's `unknown-clock`** withholds Vikunja's task create and Homebox's tag create in main's recordings in both
|
||||
builds, so those tasks fail in both. Rebuilt by #7274 at `4ede1305`, before that rule, this branch without the
|
||||
linked-path change made main's Vikunja create task pass with both models (1/3 to 2/3) and changed no other result
|
||||
among the recordings that finished.
|
||||
|
||||
## Not verified
|
||||
|
||||
- A fresh recording with skill 52, which reads each listed object by id.
|
||||
- Path parameters given as numbers stay refused: they are required properties, which REA keeps free of combinators.
|
||||
- A counter one create alone shows, and linked creates into a collection whose path has parameters, which still take
|
||||
only their recorded path values.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Implementation plan
|
||||
|
||||
1. **Blank members.** `WriteBody.blank` and its schema; creates explain empty members as blank; merges join them;
|
||||
created fields apply a left-out blank member as sent.
|
||||
2. **Listed ids.** `listed_ids` on list views and its schema; the gate learns them; list rendering sets them.
|
||||
3. **Sorted collections.** The replayed state moves an entity whose sorted field changed; update plans carry the
|
||||
type's sort; serving moves updated entities; a failed whole-collection read after an update can teach the sort.
|
||||
4. **Observations.** A create's item read-back accepts unloaded answer values; updates treat empty-container
|
||||
respellings as unchanged; later creates check unruled fields.
|
||||
5. **Skill, schema, tests and docs.**
|
||||
@@ -0,0 +1,56 @@
|
||||
# Serve the writes callers make without recorded form fields
|
||||
|
||||
Issue: https://github.com/morluto/rea/issues/4933
|
||||
|
||||
## Outcome
|
||||
|
||||
Agents now record writes through an application's UI, and environments built that way refused most of the creates
|
||||
natural-language tasks ask for, or withheld them:
|
||||
|
||||
- A form sends every field it shows, the ones left alone as `""`, `null`, `false`, `[]` or `{}`. Every member a
|
||||
recorded create sent was required, so a call naming only a title was refused (Homebox tags required `color` and
|
||||
`icon`, Linkding bookmarks `notes`, `unread` and `shared`, EspoCRM contacts about twenty members).
|
||||
- Paperless-ngx answers lists with `all`, the ids of the listed entities in list order. The state did not render it,
|
||||
so the first list read after every Paperless create contradicted the create.
|
||||
- Grafana lists folders by title and Paperless lists tags by name. An update kept its entity where it was, so a rename
|
||||
that sorted elsewhere contradicted the update, and a served rename listed in the wrong order.
|
||||
- A Vikunja create answers before it loads what it stored: the owner with empty fields and Go's zero time, and empty
|
||||
lists as `[]` where reads show `null`. The later item read was not taken as the observation, so it contradicted the
|
||||
create; an update whose answer spelled an empty list `[]` was unexplained.
|
||||
|
||||
## Behaviour
|
||||
|
||||
- **Blank members.** A create member a recorded call sent empty is not required. A call that leaves it out is applied
|
||||
as sending that value (`body.blank`), so the entity, the answer and every rule reading the member come out as for
|
||||
the recorded call. A member a later call left out takes the default it showed instead. A call may send a member as
|
||||
any recorded call sent it empty, such as `null` where another stored a string, even once a default is learned, and
|
||||
one leaving it out without a default is applied as the earliest did. Updates are unchanged.
|
||||
- **Listed ids.** A list view names top-level envelope members that list two or more listed entities' identity values
|
||||
in list order (`listed_ids`). A rendered list sets them to its own entities' values, unless it is a page of more
|
||||
matches, which keeps the recorded value as a partial page's total does.
|
||||
- **Sorted collections.** An update that writes the field its type's creates are sorted by moves its entity to where
|
||||
the new value sorts, in replay and serving (a bulk update moving each entity in turn), and its plan carries that
|
||||
`insert`. When a read listing the whole collection in collection order (not sorted by its query) fails after an
|
||||
update and the type's creates are not sorted by a
|
||||
field the update writes, replay tries the collection sorted by such a field when the entities no exercised write
|
||||
changed already follow it, as serving moves only written entities, and unless a create was placed by a sort
|
||||
that excludes it or a whole-collection read in collection order (not sorted by its query) that placed a create did
|
||||
not follow it; on a match, the type's creates and updates take that order.
|
||||
- **Unloaded answers.** A create's later item read is taken as its observation when it differs from the answer only in
|
||||
values the answer had not loaded: empty, zero, `false` or Go's zero time, or objects whose members are each the same
|
||||
or loaded so; the plan points to those values (`observation.read.unloaded`), which a served create's answer keeps as
|
||||
the recorded answer showed them, unless the call sent one the plan stores as sent, while the state holds the read's. An unsent update field that changes only between
|
||||
`null`, `[]` and `{}` is unchanged. Write answers, bulk answers and replay's checks of them included, spell empty
|
||||
lists and maps the call did not send as their recorded answers did, and a sent one as sent.
|
||||
- **Unruled fields.** A later create that stored a field no rule writes otherwise than the plan keeps it is
|
||||
`unexplained-change` at the create, so its recorded entity is held and later reads check other writes as before.
|
||||
- **Skill.** The two scratch creates differ in what they send, the second only what a person must give, however they
|
||||
are made; the skill shows how to read signed-in pages' accessibility trees from a scenario, and re-records once with
|
||||
request steps a UI write whose body the browser did not expose.
|
||||
|
||||
Plans and views that need none of this are unchanged.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- No write tool of the 109 recorded captures goes from served to withheld.
|
||||
- Rebuilt from the same recordings, the branch passes at least the write tasks main passes.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Learn blank create members and apply them when a call leaves them out.
|
||||
- [x] Learn and render list envelope members listing the listed ids.
|
||||
- [x] Move updated entities in sorted collections, in replay and serving, and learn a sort from a rename.
|
||||
- [x] Take item read-backs over unloaded create answers and empty-container respellings.
|
||||
- [x] Withhold later creates that store an unruled field otherwise.
|
||||
- [x] Add view, serving and unit tests.
|
||||
- [x] Document the rules and view member, and update the skill.
|
||||
- [x] Measure the recorded captures and served write tasks.
|
||||
- [x] Run static, formatting, documentation and test gates.
|
||||
@@ -0,0 +1,124 @@
|
||||
# Verification
|
||||
|
||||
Executed on macOS with Node v24.22.1 at `5a3c6762` (main `99062fea`), and on Ubuntu 24.04 VMs with snap Chromium for
|
||||
the agent recordings.
|
||||
|
||||
## Gates
|
||||
|
||||
- `npm run -s check:fast`, `format:check`, `scan:todos`, `docs:check`, `verify:test-discovery`, `mcp-catalog:check` and
|
||||
`jscpd`: passed. `npx knip` reports one unused export, `isToolFamilyId` in `src/contracts/toolFamilies.ts`, which this
|
||||
branch does not touch and main reports too.
|
||||
- `npx vitest run --project domain --project services --project adapters`: 18,281 passed, 25 skipped, 1 failed ("stops
|
||||
Inspector target authorization after the current stat settles"), which fails the same way on main in this environment.
|
||||
- `npm run -s test:focused --` the environment build, export and serve CLI boundaries, the analysis-view CLI and MCP
|
||||
boundaries and the environment serve MCP boundary: 7 files, 75 passed. `outputSchemaProfile.test.ts`: 4 passed; the
|
||||
full catalog grows 648 bytes and the compact catalog, 554,640 bytes, does not change.
|
||||
|
||||
## Tests
|
||||
|
||||
- View: form creates whose empty members differ require only the member both filled and apply the others as sent; a
|
||||
create with only a last name, sending `null` for strings and for a flag another create sent `false`, merges and is
|
||||
served; a rename moves a label in a collection its
|
||||
creates sort by name; a rename that sorts first teaches a folder collection's title order to its creates and updates;
|
||||
a list envelope's `all` is learned and creates there are confirmed; a create answer with an unloaded owner and `[]`
|
||||
for `null` takes its item read; an update answer spelling an empty list `[]` is unchanged; a later create storing a
|
||||
derived field otherwise is withheld at the create and later deletes stay confirmed.
|
||||
- Serving: a label created from its name alone stores the empty members as sent and is inserted by name, and one
|
||||
without the required name is refused; renamed labels and folders move to where their new names sort, and a bulk
|
||||
rename of two labels moves each; a created tag's id appears in the list envelope's `all` in list order; a note sent
|
||||
`null` as one recording did is accepted after a later call taught `""` as its default, which a note left out takes;
|
||||
a created and an updated task answer `assignees: []` as their recordings did while reads show `null`.
|
||||
- Review cases: a member's recorded empty value stays accepted once a default is learned; a rename teaches title order
|
||||
where the creates fit both id and title order; moving written entities in turn sorts `[a, b, c, d]` with `b→h` and
|
||||
`c→i` as `[a, d, h, i]`. The plan and serving tests for these fail on the branch's first head `0ceff6fa`.
|
||||
|
||||
## Write plans over recorded captures
|
||||
|
||||
The 109 captures the saved-values branch measured: 50 published captures and 59 Claude Code recorded from a prompt,
|
||||
built by main and by this branch.
|
||||
|
||||
| Captures | Write plans | Served on main | This branch |
|
||||
| ------------------------- | ----------- | -------------- | ----------- |
|
||||
| 50 published | 187 | 70 | 70 |
|
||||
| 59 recorded from a prompt | 337 | 177 | 209 |
|
||||
|
||||
No write goes from served to withheld. The 32 newly served are 23 Paperless creates (tags, correspondents and document
|
||||
types, every recording), 3 Vikunja label creates, a Vikunja task update, Grafana's folder rename and delete, a Homebox
|
||||
tag delete, and a Linkding bundle update and delete. 65 plans require fewer members and 66 carry `body.blank`; 30 plans
|
||||
change `insert`, mostly updates that now move their entity by the field their type's creates sort by.
|
||||
|
||||
## Agent-built environments
|
||||
|
||||
Claude Code (`claude-sonnet-5-5`) built an environment of each of seven applications from the same prompt, as the
|
||||
saved-values branch measured, and each bundle's write tasks ran with `fireworks_ai/gpt-oss-120b` and
|
||||
`fireworks_ai/glm-5p3-flash`. Each task checks its result from the environment's state, including that nothing else
|
||||
changed.
|
||||
|
||||
The Homebox and Linkding task specs used until now checked for seed objects an earlier round's recordings left behind
|
||||
("Rec Fragile", `scratch-two.example.test`), which no current recording holds, so their "nothing else changed" check
|
||||
could not pass. They now check the current seed. With the corrected specs, main passes both Homebox and both Linkding
|
||||
tasks on the saved-values branch's recordings, so earlier rounds' 0/2 for those applications measured the specs, not
|
||||
the required members.
|
||||
|
||||
**The same recordings, built by main and by this branch** (gpt-oss, GLM):
|
||||
|
||||
| Recording | Write tasks | Main | This branch |
|
||||
| ----------------------------- | ----------- | --------- | ----------- |
|
||||
| Paperless, saved-values | 3 | 1/3 2/3 | 3/3 3/3 |
|
||||
| Paperless, main (two) | 3 + 3 | 3/6 3/6 | 5/6 5/6 |
|
||||
| Grafana, saved-values | 2 | 0/2 0/2 | 1/2 1/2 |
|
||||
| Grafana, main (two) | 2 + 2 | 3/4 3/4 | 4/4 3/4 |
|
||||
| Vikunja, saved-values + main | 5 + 5 | 3/10 3/10 | 3/10 3/10 |
|
||||
| EspoCRM, saved-values + main | 3 + 3 | 1/6 1/6 | 1/6 1/6 |
|
||||
| Homebox, saved-values + main | 2 + 2 | 2/4 2/4 | 2/4 2/4 |
|
||||
| Linkding, saved-values + main | 2 + 2 | 2/4 2/4 | 2/4 2/4 |
|
||||
| Miniflux, saved-values | 3 | 3/3 3/3 | 3/3 3/3 |
|
||||
| Total | 42 | 18 19 | 24 23 |
|
||||
|
||||
No recording passes fewer tasks with this branch. Paperless creates pass once served. The saved-values Grafana
|
||||
recording's rename is served and passes; its create still needs the saved-values branch's composed address.
|
||||
Vikunja's task create, EspoCRM's leftover contact (held as seeded data, so every "nothing else changed" check fails)
|
||||
and the main Homebox recording, which wrote no tags, are unchanged.
|
||||
|
||||
**Fresh recordings.** Each application was recorded afresh with main (skill 49) and with this branch. The branch was
|
||||
recorded again for the four applications whose first branch recording passed fewer tasks, and a third time after the
|
||||
skill's sixth step was reworded; the final skill's recordings are the last column (served writes, then tasks):
|
||||
|
||||
| Application | Write tasks | Main | Branch, first two recordings | Branch, final skill |
|
||||
| ----------- | ----------- | -------------------- | ---------------------------- | -------------------- |
|
||||
| Paperless | 3 | 2, 1/3 2/3 | 8, 1/3 2/3 | |
|
||||
| Linkding | 2 | 0, 0/2 0/2 | 5, 2/2 2/2 | |
|
||||
| Miniflux | 3 | no bundle | 4, 3/3 3/3 | |
|
||||
| Grafana | 2 | 3, 2/2 2/2 | 1, 0/2 1/2; 3, 1/2 2/2 | 0, 0/2 0/2 |
|
||||
| Vikunja | 3 + 2 | 3, 1/3 1/3 + 2/2 2/2 | 0, 0/5 0/5; 0, 0/5 0/5 | 1, 1/3 1/3 + 0/2 0/2 |
|
||||
| EspoCRM | 3 | 2, 1/3 1/3 | 2, 1/3 1/3; 2, 1/3 1/3 | 2, 1/3 1/3 |
|
||||
| Homebox | 2 | 4, 2/2 2/2 | 1, 0/2 0/2; 1, 0/2 0/2 | 3, 2/2 2/2 |
|
||||
|
||||
Every fresh recording builds the same plans with main and with this branch, except Paperless (main serves 0 of the
|
||||
branch recording's writes and 2 of 3 of its own), Vikunja's main recording (3 to 4) and the EspoCRM recordings below;
|
||||
the per-application differences are what each agent recorded:
|
||||
|
||||
- **Paperless:** the branch recording serves creates, renames and deletes of tags, correspondents and document types.
|
||||
Its two tag creates used a light and a dark color, whose derived `text_color` differs, so the tag create stays
|
||||
withheld and the tasks, which create a tag, score as on main.
|
||||
- **Linkding:** main's agent recorded no writes. The branch's second bookmark create sent only `url` and `title`, so
|
||||
the served create requires only those.
|
||||
- **Miniflux:** main's agent stopped and asked for an API key. The branch's agent created a token in the UI and sent it
|
||||
as a literal header value, which REA recorded as written; the instance and its token were destroyed afterwards.
|
||||
- **EspoCRM:** following the sixth step, the branch's second contact sent only a last name, with `null` for strings the
|
||||
first stored and for flags it sent `false`. That merge was refused until the empty-spelling fix in this PR; with it,
|
||||
the create requires only `lastName` and `name` and its task passes, as on main.
|
||||
- **Vikunja:** the first two branch agents wrote through the UI, whose request bodies Chromium did not expose, so every
|
||||
write was `body-unusable`. The sixth step then read "fill every field the form offers"; reworded to say how the
|
||||
creates differ, not where they are made, the third agent wrote with request steps. It recorded no label writes.
|
||||
- **Homebox:** the first two branch agents recorded item writes only, the third tags as main's did.
|
||||
- **Grafana:** the branch agents created folders with request steps and their own uids. The second create's address
|
||||
needs the saved-values branch's composed-address fixes to be explained, so the create is withheld here.
|
||||
|
||||
## Not verified
|
||||
|
||||
- One to three fresh recordings per application and version; agent runs vary more than the builds do.
|
||||
- Request bodies Chromium does not expose for some UI writes (Vikunja's); the skill now re-records such a write once
|
||||
with request steps.
|
||||
- Paperless derives `text_color` from `color`, so a create whose two recorded colors differ in brightness stays
|
||||
withheld; EspoCRM's create still requires `name` and `emailAddressData`, which it derives from other members.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Implementation plan
|
||||
|
||||
1. **Soft deletes.** `markedDeleted` reads the deleted flags; item-read checks, adoption and replay's resync treat a
|
||||
marked object as absent.
|
||||
2. **Page sizes.** Add the names to the paging and query `limit` sets.
|
||||
3. **Later fields.** Extraction collects the members later plain item reads show; the gate adds those no merged entity
|
||||
holds to the item view without requiring them.
|
||||
4. **Answers.** `laterItemFields` names the item view's fields no initial entity holds; write answers and the verdict's
|
||||
`incomplete-entities` check skip them for entities without them.
|
||||
5. **Skill, tests and docs.**
|
||||
@@ -0,0 +1,42 @@
|
||||
# Serve the writes that later reads explain
|
||||
|
||||
Issue: https://github.com/morluto/rea/issues/4933
|
||||
|
||||
## Outcome
|
||||
|
||||
Environments built from a prompt withheld some writes in every recording of an application, though the recordings read
|
||||
each write back:
|
||||
|
||||
- EspoCRM keeps a deleted record. A read of it answers `200` with the record and `deleted: true`, which replay took
|
||||
as the record still being there, so contact and task deletes were contradicted or left unconfirmed.
|
||||
- EspoCRM pages its lists with `maxSize`, which REA did not know as a page size, so a list read after a delete was not
|
||||
taken as listing the whole collection and confirmed nothing.
|
||||
- Vikunja shows `subscription` only on tasks created after the recording started. The tasks' item view did not show
|
||||
it, so reads of created tasks disagreed with the state and contradicted the task update before them; and an update
|
||||
whose recorded answer shows it could not answer for a seeded task, which lacks it.
|
||||
|
||||
## Behaviour
|
||||
|
||||
- **Soft deletes.** An item read whose object is marked deleted (`deleted`, `isDeleted` or `is_deleted` set to `true`)
|
||||
agrees with an entity the state does not hold, as a `404` does. Replay drops the entity such a read names, and such
|
||||
an object never seeds or adopts an entity. The item view records the flag its reads set as `deleted_flag`, and
|
||||
serving keeps an entity a served delete removed from such a type until a reset or load, so a read of it by id answers
|
||||
its last fields with the flag `true`, as the application does.
|
||||
- **Page sizes.** `maxSize`, `max_results` and `maxResults` are page sizes, for a list read's whole-collection
|
||||
membership and for a list query's `limit` role.
|
||||
- **Fields only later reads show.** An item view also shows the members that reads after its type's first write show
|
||||
of the entities they name, when no initial entity holds them. They are never required. An update or delete that
|
||||
answers with the entity, a bulk update's answer included, leaves such a field out when the entity does not hold it,
|
||||
and the field no longer makes the write `incomplete-entities`. A field some initial entity holds keeps the existing rule: an answer showing it is
|
||||
refused for an entity without it.
|
||||
- **Skill.** Updates change the fields a person would change (a name, an email address, a status, a due date) rather
|
||||
than only a description, since a served update accepts only the members a recorded update stored, and a deleted
|
||||
object may read back marked deleted instead of `404`.
|
||||
|
||||
Plans and views that need none of this are unchanged.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- No write tool of the 130 recorded captures goes from served to withheld, and no plan's rules, body or `insert`
|
||||
changes.
|
||||
- Rebuilt from the same recordings, the branch passes at least the write tasks #7260 passes.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Treat item reads of objects marked deleted as absent in checks, adoption and replay.
|
||||
- [x] Answer a read of a served-deleted entity of such a type marked deleted, until a reset or load.
|
||||
- [x] Learn `maxSize`, `max_results` and `maxResults` as page sizes.
|
||||
- [x] Add fields only later item reads show to item views, unrequired.
|
||||
- [x] Leave those fields out of write answers for entities without them, and stop them withholding updates.
|
||||
- [x] Add view, serving and unit tests.
|
||||
- [x] Document the rules and update the skill.
|
||||
- [x] Measure the recorded captures and served write tasks.
|
||||
- [x] Run static, formatting, documentation and test gates.
|
||||
@@ -0,0 +1,84 @@
|
||||
# Verification
|
||||
|
||||
Executed on macOS with Node v24.22.1 on this branch, built on #7260's head `053264bd` (main `662ec2e4`), and on
|
||||
Ubuntu 24.04 VMs for the task runs.
|
||||
|
||||
## Gates
|
||||
|
||||
- `npm run -s check:fast`, `scan:todos`, `docs:check`, `verify:test-discovery`, `mcp-catalog:check` and `jscpd`:
|
||||
passed.
|
||||
- `npx oxfmt --check .` reports `scripts/verify-apk-signature.mjs` only, which this branch does not touch and
|
||||
`053264bd` reports too. `npx knip` reports `isToolFamilyId` in `src/contracts/toolFamilies.ts` only, as main does.
|
||||
- `npx vitest run --project domain --project services --project adapters`: 18,420 passed, 25 skipped, 1 failed ("stops
|
||||
Inspector target authorization after the current stat settles"), which fails the same way on main in this
|
||||
environment.
|
||||
- `npm run -s test:focused --` the environment build, export and serve CLI boundaries, the analysis-view CLI and MCP
|
||||
boundaries and the environment serve MCP boundary: 7 files, 75 passed.
|
||||
|
||||
## Tests
|
||||
|
||||
- View: a contact delete whose item read answers the record with `deleted: true` is served, confirmed by that read and
|
||||
the list; only a `true` `deleted`, `isDeleted` or `is_deleted` marks an object deleted; a delete is confirmed by a
|
||||
list read paged with `maxSize`; a field only created tasks show joins the task item view without being required; a
|
||||
task update whose answers show it is served.
|
||||
- Serving: a deleted contact leaves the served list; an update of a seeded task, single or bulk, answers without the
|
||||
field only created tasks show (the bulk case fails on `4515a000`, before the review fix).
|
||||
- The six behaviour tests fail on `053264bd`.
|
||||
- The existing serving test that refuses to give chapter rows to a book the state holds without them still passes. A
|
||||
first version of the answer rule left out every field the item view does not require, which served that update; the
|
||||
rule now covers only fields no initial entity holds.
|
||||
|
||||
## Write plans and views over recorded captures
|
||||
|
||||
The 109 captures #7260 measured and #7260's 21 fresh recordings, built by `053264bd` and by this branch:
|
||||
|
||||
| Captures | Write plans | Served on #7260 | This branch |
|
||||
| ------------------------- | ----------- | --------------- | ----------- |
|
||||
| 50 published | 187 | 70 | 71 |
|
||||
| 80 recorded from a prompt | 428 | 258 | 268 |
|
||||
|
||||
- No write goes from served to withheld, and no plan's rules, body or `insert` changes. The 11 newly served are
|
||||
EspoCRM contact deletes in seven recordings, the published EspoCRM capture's task delete, and Vikunja task updates
|
||||
in three recordings.
|
||||
- With main `c6893d1a` merged, whose `unknown-clock` withholds clock writes without an anchor on both builds, #7260
|
||||
serves 322 and this branch 332. One of the Vikunja task updates is among the writes it withholds, so this branch
|
||||
gains 10, and none goes from served to withheld.
|
||||
- 73 list views learn a page size they held as `inert`, valid only for its recorded values, as `limit`: 72 EspoCRM
|
||||
lists (`maxSize`) and one Umami list (`maxResults`). One EspoCRM stream list now counts a line paged only by
|
||||
`maxSize` as listing the whole collection and explains its other line's query; no view explains fewer lines.
|
||||
- Six item views gain a field only later reads show, never required: Vikunja's tasks `subscription` in four recordings,
|
||||
a Homebox entity's `children` and Mattermost's posts `has_reactions`. Every other view is unchanged.
|
||||
|
||||
## The same recordings, built by #7260 and by this branch
|
||||
|
||||
Claude Code (`claude-sonnet-5-5`) recordings from earlier rounds, rebuilt by each version, their write tasks run with
|
||||
`fireworks_ai/gpt-oss-120b` then `fireworks_ai/glm-5p3-flash`, each checked from the environment's state:
|
||||
|
||||
| Recording | Write tasks | #7260 | This branch |
|
||||
| ---------------------------------------------- | ----------- | ----------- | ----------- |
|
||||
| EspoCRM: main, #7260 (three), saved-values | 5 × 3 | 5/15 5/15 | 15/15 10/15 |
|
||||
| EspoCRM: saved-values, leftover contact | 3 | 0/3 0/3 | 0/3 0/3 |
|
||||
| Vikunja: main, #7260 final, saved-values (two) | 4 × (3 + 2) | 8/20 8/20 | 8/20 8/20 |
|
||||
| Homebox: main | 2 | 2/2 2/2 | 2/2 2/2 |
|
||||
| Total | 40 | 15/40 15/40 | 25/40 20/40 |
|
||||
|
||||
No recording passes fewer tasks with this branch.
|
||||
|
||||
- **EspoCRM:** the contact delete passes with both models on all five recordings. The contact update passes with
|
||||
gpt-oss on all five only by replacing the contact: the served update refuses `emailAddress`, which no recorded update
|
||||
stored, so the agent created a contact with the new address and deleted the old one, which the task's check, by name
|
||||
and address, accepts. The update itself is refused in both builds, as is a read of a seeded contact by id, since the
|
||||
state knows seeded contacts only from list reads, which lack fields the item reads show. GLM's update fails in both
|
||||
builds the same way, and it often ends a turn at the eval agent's 1,024-token completion limit.
|
||||
- **EspoCRM, leftover contact:** the recording started with a contact an earlier attempt left, held as seeded data, so
|
||||
every "nothing else changed" check fails in both builds.
|
||||
- **Vikunja:** the newly served task update accepts the members the recorded update stored (`title`, `description`,
|
||||
`priority`), so the task asking to mark a task done is refused in both builds. The skill now asks for updates that
|
||||
change a status, an email address or a due date. Task creates are unchanged.
|
||||
- **Homebox:** the entity view's new `children` field changes no task.
|
||||
|
||||
## Not verified
|
||||
|
||||
- An application answering `deleted: true` for a record it still lists, as a trash view might: such a read counts as
|
||||
absent. No recorded capture does this.
|
||||
- The skill's update wording, which no fresh recording has used yet.
|
||||
+5
-1
@@ -210,7 +210,11 @@ changing a callback solely to satisfy its declared `void` return type.
|
||||
Ordinary Knip remains. `check`, `check:pr`, and `check:ci:deep` run reachability
|
||||
then typed lint sequentially after cached work. Typed lint builds the project
|
||||
type graph and stays separate from fast feedback. Its default Go settings are
|
||||
`GOMAXPROCS=2` and `GOMEMLIMIT=1024MiB`; caller-selected settings take precedence.
|
||||
`GOMAXPROCS=1` and `GOMEMLIMIT=1024MiB`; caller-selected settings take precedence.
|
||||
One Go worker reuses checker caches across the file queue without changing the
|
||||
file inventory or TypeScript project scope. The Go memory target is soft and
|
||||
does not cap the complete process family's memory; measure the full invocation
|
||||
when enforcing a host budget.
|
||||
|
||||
Routine iterations and rebases need focused regressions and relevant checks.
|
||||
Before handing off a PR, run `npm run check` and generated-document checks when
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# U-Boot legacy uImage containers
|
||||
|
||||
`inspect-artifact` and `extract-artifact` recognize the big-endian
|
||||
`0x27051956` legacy uImage signature regardless of filename. They read local
|
||||
stored bytes without booting, executing, decompressing or modifying the image.
|
||||
FIT is a different format; its analysis is outside this reader.
|
||||
|
||||
The same domain header/table facts and artifact reader serve CLI and MCP.
|
||||
The root is an archive with unknown executable architecture. The header's CPU,
|
||||
OS, type, compression, load address and entry address are declarations, not proof of
|
||||
compatible or executable payloads. Raw numeric identifiers remain available
|
||||
alongside names; unrecognized identifiers have the name `unknown`.
|
||||
|
||||
The header's CRC32 and the declared payload's CRC32 must verify before a
|
||||
successful inventory. `payload.bin` contains the complete stored payload.
|
||||
Multi-file images also expose `image-0.bin`, `image-1.bin`, and so on. These
|
||||
component names are generated from table order. Components follow the zero-terminated big-endian size table, with four-byte
|
||||
padding after every nonfinal component. Their physical source ranges and
|
||||
content hashes retain precise provenance. Extraction writes the complete
|
||||
payload and every component, so multi-file output can approach twice the input
|
||||
size. Compressed payloads remain compressed.
|
||||
|
||||
The fixed 32-byte name field is retained as `name_bytes_hex`. `name` contains
|
||||
the UTF-8 bytes before the first NUL, or null when those bytes cannot be decoded
|
||||
exactly. For example, a producer can truncate a multibyte character at the
|
||||
32-byte boundary; replacement characters do not establish a source name.
|
||||
|
||||
## Bounded work and retained facts
|
||||
|
||||
The reader admits at most 4,096 components, a resource policy rather than a
|
||||
format maximum. It reads at most 16,388 bytes for the size table and refuses an
|
||||
excess count as a capacity limit before its remaining payload CRC scan.
|
||||
Shared artifact identity hashing has separate cost. The
|
||||
same prefix participates in the native incremental CRC32 calculation; no
|
||||
custom bytewise CRC table or full-payload buffer is retained. Remaining CRC
|
||||
reads and member streams use 64 KiB chunks. Parsed facts are cached for one reader. Carrier state is checked during
|
||||
directory preparation, at member open and after stream completion; metadata
|
||||
getters return the retained facts. Close joins active member
|
||||
pipelines and final validation before releasing the descriptor; failed cleanup
|
||||
remains owned for retry.
|
||||
|
||||
Prefer a retained summary when only a few facts are needed:
|
||||
|
||||
```sh
|
||||
rea inspect-artifact firmware.bin --detail summary \
|
||||
--evidence_output firmware.evidence.json --json
|
||||
rea extract-artifact firmware.bin --json
|
||||
```
|
||||
|
||||
With MCP, `inspect_artifact` with `detail: "summary"` retains the complete
|
||||
Evidence. Use `inspect_analysis_view` and its `parent_evidence_id` for exact
|
||||
logical paths, batches of occurrence/content IDs, selected fields and pages.
|
||||
Coverage and limitations remain attached. Retained views are historical
|
||||
observations tied to the saved Evidence. Complete Evidence remains available;
|
||||
these views do not narrow the examined inventory.
|
||||
|
||||
## Verification scope
|
||||
|
||||
The public OpenWrt 24.10.0 GL-AR150 initramfs kernel is checked against its
|
||||
[published digest](https://downloads.openwrt.org/releases/24.10.0/targets/ath79/generic/sha256sums):
|
||||
`027c60a7061462d8145b11efa23ab02b334acb9a23a215f9095860b632ace4ff`.
|
||||
Independent `mkimage`/`dumpimage` 2023.01 provide producer and extraction
|
||||
oracles. Generated containers use real host ELF files, system metadata and
|
||||
stored gzip bytes; their declared ARM64, VxWorks or RISC-V labels do not prove
|
||||
boot/runtime compatibility. U-Boot's identifier and alignment definitions are
|
||||
pinned to commit `ece349ade2973e220f524ce59e59711cc919263f`.
|
||||
|
||||
Source and compiled CLI/full/compact MCP tests cover aligned component ranges,
|
||||
unknown identifiers/text, malformed/truncated inputs, CRC failures, capacity,
|
||||
source changes, cancellation, pending reads, final validation and cleanup
|
||||
retry. Real producer results, complete-task cost measurements and limitations
|
||||
are reported in the PR. No target runtime or decompressor behavior is verified.
|
||||
@@ -0,0 +1,86 @@
|
||||
# UnityFS AssetBundle archives
|
||||
|
||||
`inspect-artifact` and `extract-artifact` read standalone UnityFS AssetBundle
|
||||
containers without running Unity or the producer application. Header format
|
||||
versions 6 through 8 are supported. Version 9 adds a different block-table
|
||||
layout and is unsupported until independently verified.
|
||||
|
||||
Inspection retains the header facts: format version, player and engine version
|
||||
strings, declared size, dataflags, blocks-and-directory table bounds, the
|
||||
producer's 16-byte data hash as declared, and the resolved data-area offset.
|
||||
Members (`CAB-*` SerializedFiles, `.resS` resource streams, shared assets)
|
||||
retain declared source paths, virtual stream offsets, sizes and raw node flags.
|
||||
The directory flag identifies directories. Virtual offsets are not physical
|
||||
carrier locations: member `source_location` is null. Member reads only examine
|
||||
their covering blocks. Uncompressed ranges stream directly; compressed ranges
|
||||
reuse at most one completed decoded block. Independent 64 KiB output chunks
|
||||
preserve cache ownership. Carrier freshness is checked when a member is opened,
|
||||
including when a cached block suffices, and after its stream completes. Reader close cancels and
|
||||
joins outstanding streams before releasing its descriptor.
|
||||
|
||||
Data blocks compressed with none, LZ4 or LZ4HC are supported. LZMA-compressed
|
||||
blocks and blocks-info tables are reported as unsupported coverage or an
|
||||
`unavailable` member read; the directory and any readable members are still
|
||||
retained. Bundles declaring asset-bundle encryption keep their header facts
|
||||
but report unknown directory coverage; no decryption is attempted. The reused
|
||||
0x200 encryption/padding flag is interpreted by producer revision. Ambiguous
|
||||
revisions retain unknown layout coverage. Unread block counts and data offsets
|
||||
are null, never invented zeroes. A declared
|
||||
size that exceeds the input file, node paths outside the block stream, and
|
||||
counts that cannot fit their table are malformed-input errors.
|
||||
|
||||
## Resource bounds
|
||||
|
||||
These are resource policies, not format maxima. Encoded and decoded directory
|
||||
buffers are each limited to 16 MiB. Buffers, parsed rows, retained entries and
|
||||
path projections share a conservative 64 MiB admission budget, reserved before
|
||||
allocation. Each LZ4 decode allows at most 64 MiB of output and its bounded
|
||||
encoded representation; plain blocks stream without this allocation. A reader
|
||||
admits four active member streams. The shared archive plaintext-work ceiling
|
||||
is the larger of 256 MiB and 500 times carrier size: declared stream bytes,
|
||||
readable logical occurrences (including aliases), and cumulative block-decode
|
||||
attempts are each charged against that ceiling. Empty-output blocks retain
|
||||
their source count and offsets but are removed from member read plans. A separate
|
||||
member-I/O budget allows four times the larger of 1,024 and the sum of nonempty
|
||||
blocks, directory nodes, and readable 64 KiB plaintext chunks. It admits ordinary
|
||||
passes while bounding repeated tiny-block reads independently of output bytes. Unavailable content is retained
|
||||
as named occurrences with unknown coverage and no invented content hash.
|
||||
|
||||
Use existing summary delivery and retained inventory views for selected fields,
|
||||
filters and paging; full Evidence remains available without repeating the graph.
|
||||
|
||||
## CLI
|
||||
|
||||
```sh
|
||||
rea inspect-artifact assets/hotfix.bundle --detail summary \
|
||||
--evidence_output evidence/hotfix.json --json
|
||||
rea extract-artifact assets/hotfix.bundle --json
|
||||
```
|
||||
|
||||
Detection is byte-based on the `UnityFS\0` signature, so `.bundle`, `.ab`,
|
||||
`assets_*` YooAsset bundles and extensionless files all classify as `unityfs`
|
||||
containers. Compact delivery keeps complete Evidence in the requested bundle.
|
||||
|
||||
## Verification scope
|
||||
|
||||
Synthetic fixtures cover format 6–8 layouts, padding independent of header
|
||||
alignment, directories, malformed tables, allocation admission, overlapping
|
||||
members, unsupported LZMA, cancellation inside decoding, paused iteration,
|
||||
close during I/O, cleanup retry, empty blocks with unsupported codecs, cache
|
||||
freshness and independent output buffers.
|
||||
Compiled CLI and full/compact MCP tests cover inventory and extraction.
|
||||
|
||||
A public UnityPy `atlas_test` bundle (Unity 2018.4.11f1, format 6) is verified
|
||||
against independent UnityPy 1.24.2 raw member bytes. Source SHA-256:
|
||||
`61ee5afb5f5c64f0a4f65f039d09b2509198363e713423289c895067ac215177`.
|
||||
The source is pinned to UnityPy commit
|
||||
`417d59522a998fdee9a9c2b29fe35f3063222457`. The same public corpus supplies
|
||||
`banner_1` (2018.4.4f1) and `char_118_yuki.ab` (5.6.7f1), whose raw members
|
||||
also match the independent oracle. `xinzexi_2_n_tex` (2017.4.37f1) uses LZMA
|
||||
data: its member names and sizes are retained with unavailable hashes and
|
||||
unknown coverage. UnityPy-produced repacks additionally verify format 7/8,
|
||||
padded and unpadded front tables, end tables, and unsupported LZMA directories.
|
||||
These repacks are library-produced tests, not Unity engine executions. The PR
|
||||
records producer parameters, source digests and measured task costs; synthetic
|
||||
fixtures alone do not establish real Unity version coverage. SerializedFile objects, textures and runtime
|
||||
behavior are outside this container reader's verification scope.
|
||||
@@ -15,7 +15,7 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
|
||||
11,src/domain/errors.ts:ArtifactOperationError,Artifact inventory or extraction fails,"Artifact {operation} failed: {reason} at {logicalPath} (declared_sha256=...)",Exposes paths hashes and internal reason,"Reason-specific plain message with safe recovery step.",implemented,focused assertions passed; full `npm run check` passed
|
||||
12,src/domain/errors.ts:EvidenceFileError,Evidence file operation fails,"Evidence bundle {operation} failed: {reason}",Internal reason token gives weak recovery,"Reason-specific plain message with safe recovery step.",implemented,focused assertions passed; full `npm run check` passed
|
||||
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
|
||||
14,src/domain/analysisErrorPresentation.ts:UnknownRegistryError,Residual-unknown reference or graph constraint fails,Specific graph constraint and missing reference ID,Unchanged retries cannot repair missing references or insufficient proof,Correct the named reference or proof constraint; revision conflicts retain refresh recovery.,implemented,96 focused tests passed; real stdio correction and atomic-rejection controls 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; 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, provider diagnostics, and reported cleanup, then follow the returned remediation. Change a request field or REA_* setting only when the returned remediation explicitly recommends that change. Restart REA when that text says the value is read at startup. A mention that a control does not raise the deadline is not a recommendation to change it. When it names no setting, reduce supported work; do not invent one. JavaScript names analysis_timeout_ms and exact source_paths; omit an unneeded filter rather than passing an empty array.",implemented,generic projection does not invent a setting; producer remediation names analysis_timeout_ms or the applicable REA_* value; initialization and the shipped skill teach the same split
|
||||
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
|
||||
@@ -44,13 +44,15 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
|
||||
32,src/application/Setup.ts:configureDetectedClients(backup),Agent configuration backup fails,"{clientName} configuration backup verification failed; no successful configuration was reported.",Exposes machine client key and obscures no-write guarantee,"Agent configuration could not be backed up, so no change was made. Check file permissions, then rerun setup.",implemented,focused assertions passed
|
||||
33,src/application/Setup.ts:configureDetectedClients(write),Agent configuration write fails,"{clientName} configuration write verification failed; no successful configuration was reported.",Exposes machine client key and gives no recovery,"Agent configuration could not be updated. Check file permissions, then rerun setup.",implemented,focused assertions passed
|
||||
34,src/application/Setup.ts:configureDetectedClients(readback),Written agent configuration cannot be verified,"{clientName} configuration readback verification failed; no successful configuration was reported.",Exposes machine client key and omits backup recovery,"Agent configuration could not be verified after writing. The original was restored and its backup at {backupPath} was retained. Repair the configuration file or restore that backup, then rerun setup. Without a backup: Agent configuration could not be verified. Repair the configuration file, then rerun setup.",implemented,focused assertions passed
|
||||
35,src/application/Setup.ts:installSkill,Skill install or semantic readback fails,Agent skill installation or readback failed.,No useful recovery action,"REA analysis skill could not be installed or verified. Check permissions for `~/.agents/skills`, then rerun setup.",implemented,focused assertions passed
|
||||
36,src/main.ts:serveStdio.onerror,Established MCP client connection reports an error,MCP stdio transport error,Exposes transport implementation and gives no recovery,"Retry the tool call once only when it is safe to repeat or known not to have started. If it may already have changed the target, or it fails again, tell the user REA lost its MCP client connection and ask them to restart the MCP client.",implemented,verified by injected transport onerror runtime test
|
||||
35,src/application/SetupSkill.ts:SetupSkillInstallError,Skill install or semantic readback fails,Stage and selected path; separate failure reason and recovery action,A non-directory path is not a demonstrated permission failure,Correct the reported failure; directory blockers retain regular-directory and --skill=false guidance.,implemented,"Public CLI ancestor/root refusal, preserved caller file, complete install and idempotent rerun; focused recovery tests"
|
||||
36,src/main/reportMcpTransportError.ts:reportMcpTransportError,MCP pipe breaks or connection resets,"Retry the tool call once only when it is safe to repeat or known not to have started. If it may already have changed the target, or it fails again, tell the user REA lost its MCP client connection and ask them to restart the MCP client.",Retry safety and restart guidance for known disconnection,Keep current copy,implemented,injected EPIPE recovery regression
|
||||
36,src/main/reportMcpTransportError.ts:reportMcpTransportError,MCP message fails schema validation,MCP message validation failed. Check the protocol fields before retrying a message that is safe to repeat. Continue using the connection while it remains responsive.,Validation failure does not establish connection loss,Keep current copy,implemented,public stdio invalid-ID controls followed by successful ping and file analysis
|
||||
36,src/main/reportMcpTransportError.ts:reportMcpTransportError,MCP error has no known validation or disconnection identity,MCP reported an error with unknown connection state. Check whether the client is still connected before following connection-loss recovery.,Connection state remains unknown,Keep current copy,implemented,injected unknown-error recovery regression
|
||||
37,src/main.ts:serveStdio throw,MCP connection cannot be created,Failed to start MCP stdio transport,Exposes transport implementation and gives no recovery,"Retry once. If it fails again, tell the user REA could not start its MCP connection and ask them to restart the MCP client, then run `rea doctor`.",implemented,verified by injected transport startup-throw runtime test
|
||||
38,src/main.ts:requestShutdown catch,Connection or analysis session close rejects,Failed to shut down cleanly,Does not explain duplicate-process risk or recovery,REA could not close cleanly. End the REA process before starting it again.,implemented,verified by injected handle-close rejection runtime test
|
||||
39,src/main.ts:entrypoint catch,Unhandled rejection escapes run(),Unexpected server startup failure,Vague and lacks diagnostic recovery,"Do not repeat this startup. Tell the user REA could not start and ask them to restart the MCP client, then run `rea doctor`.",implemented,verified by injected entrypoint rejection test
|
||||
40,src/application/Upgrade.ts:unknown-install-method,Running package is not owned by detectable global npm installation,Update manually with: npm install --global rea-agents@latest,Already gives exact safe recovery,Keep current copy,unchanged,existing focused assertion passed
|
||||
41,src/application/Upgrade.ts:install,npm global install subprocess exits unsuccessfully,npm install --global rea-agents@latest failed.,Does not identify bounded likely checks before retry,"REA could not update through npm. Check npm registry access and global install permissions, then run: npm install --global rea-agents@latest",implemented,focused assertion passed
|
||||
40,src/application/Update.ts:unknown-install-method,Running package is not owned by detectable global npm installation,Update manually with: npm install --global rea-agents@latest,Old global-npm advice does not identify the owning installation,"Update this installation with the package manager or source checkout that owns it. For a one-off latest invocation, use npx rea-agents@latest.",implemented,36 Update tests passed; public CLI unowned-installation refusal verified with isolated package-manager fixtures
|
||||
41,src/application/Update.ts:install,npm global install subprocess exits unsuccessfully,npm install --global rea-agents@latest failed.,Old registry/global-install advice omits the actual version prefix and installer failure,"REA could not install {version} in {prefix}: {failure}. Repair this npm installation, then rerun rea update.",implemented,36 Update tests passed; public CLI owned npm fixture failure preserved version prefix and installer reason
|
||||
42,src/application/Uninstall.ts:removeClient read,Existing agent configuration cannot be read,Configuration could not be read.,No recovery step,"Configuration could not be read. Check file permissions, then rerun uninstall.",implemented,verified by injected filesystem read failure test
|
||||
43,src/application/Uninstall.ts:removeClient parse,Agent configuration is malformed,Malformed configuration was preserved without mutation.,Does not name required repair format or retry,"Configuration is not valid JSON/TOML and was not changed. Repair it, then rerun uninstall.",implemented,focused JSON assertion passed; TOML path uses same branch
|
||||
44,src/application/Uninstall.ts:removeClient backup,Configuration backup copy fails,Configuration backup failed; no mutation was attempted.,No recovery step,"Configuration could not be backed up, so no change was made. Check file permissions, then rerun uninstall.",implemented,verified by injected filesystem backup failure test
|
||||
@@ -114,7 +116,7 @@ Then rerun this installer.",implemented,verified by shell branch inspection
|
||||
88,install.sh:binary absent,npm exits successfully without executable,the rea command was not installed at local path,Leaks local path and no recovery,"npm completed without installing the rea command. Check the npm global bin directory and PATH, then retry.",implemented,verified by exact absent-installed-command test
|
||||
89,install.sh:version read,Installed rea executable cannot report its version,could not read the installed REA version.,No recovery instruction,"The installed REA version could not be read. Reinstall the requested version, then retry.",implemented,verified by exact unreadable-installed-version test
|
||||
90,install.sh:version mismatch,Installed rea reports a different version,installed version ACTUAL does not match EXPECTED.,Low risk; gives exact integrity mismatch,Keep current copy,unchanged,verified by exact installer test
|
||||
90.5,@modelcontextprotocol/server:tool input validation,Caller sends arguments that violate a declared tool schema,"Canonical invalid_request with the SDK diagnostic plus recovery.kind=inspect_input_contract and a bounded instruction to read the advertised schema or call tools/list",Keeps field/type correction while telling the agent how to recover; diagnostics over 4 KiB stay on the existing retained-error path,Normalize ordinary validation failures and retain oversized diagnostics,verified by real in-memory and stdio MCP client/server calls with wrong argument types
|
||||
90.5,@modelcontextprotocol/server:tool input validation,Caller sends arguments that violate a declared tool schema,"Canonical invalid_request with the SDK diagnostic plus recovery.kind=inspect_input_contract and a bounded instruction to read the advertised schema or call tools/list",Keeps field/type correction while telling the agent how to recover; diagnostics over 4 KiB stay on the existing retained-error path,Normalize ordinary validation failures and retain oversized diagnostics,implemented,verified by real in-memory and stdio MCP client/server calls with wrong argument types
|
||||
90.6,incur:unknown CLI command,Caller enters an unknown REA command,"'COMMAND' is not a command for 'rea'. Suggested command: rea --help",Low risk; identifies input and exact discovery action,Keep framework-generated copy,unchanged,verified by real packaged dispatcher invocation
|
||||
90.7,src/ghidra/GhidraProviderClient.ts:projectPrimarySessionError,One procedure's decompile reaches REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS,"Ghidra decompilation of {entry} exceeded the {N}-second limit set by REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS. Only this procedure's decompilation was stopped; the Ghidra session remains usable.",Low risk; names the setting and keeps the session,Keep current copy,implemented,provider projection test and real-provider lane
|
||||
91,src/application/binaryDiagnostics/ByteEntropyService.ts,Invalid file or window,"Expected a regular file.; Selected file could not be found.; Selected byte window starts beyond the end of the file.; Selected byte window extends beyond the end of the file.; File size exceeds exact numeric offset representation.",Invalid selection cannot produce complete entropy Evidence,Choose a readable regular file and a window within its byte size,implemented,entropy service and MCP boundary tests
|
||||
|
||||
|
File diff suppressed because it is too large.
Load diff
@@ -4,10 +4,14 @@ Issue [#4772](https://github.com/morluto/rea/issues/4772). Qualified runtime
|
||||
`b49ee40bf330b7bf1c5180b14dcc104c1c613a8e`, tree `0e5f8fe2feae1796c340c07c26e0e28e5d4328d8`,
|
||||
integrates main `c8ed32853881b08aa83459d4da55a28a4e09b0c8`. Recorded at
|
||||
`2026-10-22T08:30:58.269Z`. Subsequent verification-only
|
||||
commits change these reports. The [JSON record](javascript-route-inventory-2026-10-21.json)
|
||||
commits updated these reports. The [archived JSON record](https://github.com/morluto/rea/blob/7d0efef62d56d0a10134bea62d709302daa1565f/docs/verification/javascript-route-inventory-2026-10-21.json)
|
||||
pins original inputs, exact commands, profiles, failed controls and historical revisions.
|
||||
Current-head GitHub CI and automated review must be checked separately.
|
||||
|
||||
The full JSON archive is 2,681,223 bytes, with SHA-256
|
||||
`9d7ea2582a6f33ff0741578aec9f01b9327b3256ce50c582437164868e544d51`. It remains available in Git
|
||||
at the linked immutable revision; this page retains the historical summary.
|
||||
|
||||
The latest main contracts, selected environment and ASAR admission remain shared
|
||||
by CLI and MCP. The staged-code hook runs lint fixes before formatting, resolving the
|
||||
producing order behind the earlier CI format failure.
|
||||
|
||||
@@ -670,12 +670,12 @@ Every entity cites its `record_ordinals`.
|
||||
|
||||
`views` lists one plan per read tool in tool order:
|
||||
|
||||
| Shape | Plan |
|
||||
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `item` | The entity object at `pointer` (the root, one member, or `/0` or `/<member>/0`), the ordered `fields` the view shows and the `required_fields` every recording had, and `envelope_ordinal`: the recording whose response, with the entity replaced, every item response shares, leaving out the `per_response_members` each response there mints: a top-level UUID or clock that differed on every repeat of every concrete path the view answers, with no write between, and never stayed the same on a repeat elsewhere; list views name theirs too |
|
||||
| `list` | The array at `pointer`, its `fields`, `membership` (`all`: every entity of the collection in state order, when each recording listed exactly that, had no query or only paging parameters (`limit`, `offset`, `page`, `per_page`, `perpage`, `page_size`, `pagesize`, `size`, `start`, `_start`, `_end`), no item-total member (`total`, `count`, `totalCount`, `item_count`, `totalcount`, `itemcount`, `numberOfElements`, but not `total_pages` or `per_page`; a number or a string of digits; top-level, or in a top-level object beside the list such as `pageInfo.totalRows`) differing from the number listed, and no `has_more`, `next` or next-page token member set; `recorded`: the entities each recording listed, except on the lines `all_record_ordinals` names, which each meet those conditions on their own and list every entity), and `counters` that equal the number listed |
|
||||
| `document` | The latest initial value of a list tool whose responses hold no identified list, when its initial reads share one request line; held in `documents` with that `path`, whether or not the capture writes it, unless its value changed across any recorded write between those reads (`fields-differ`); its `per_response_members` are the members of the value each response on that path mints |
|
||||
| `recorded` | A `reason` the tool replays its recordings instead |
|
||||
| Shape | Plan |
|
||||
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `item` | The entity object at `pointer` (the root, one member, or `/0` or `/<member>/0`), the ordered `fields` the view shows and the `required_fields` every recording had (the fields include those only reads after the type's first write show, of entities that hold its identity, when no initial entity holds them, as Vikunja adds `subscription` to the tasks it creates; they are never required), and `envelope_ordinal`: the recording whose response, with the entity replaced, every item response shares, leaving out the `per_response_members` each response there mints: a top-level UUID or clock that differed on every repeat of every concrete path the view answers, with no write between, and never stayed the same on a repeat elsewhere; list views name theirs too |
|
||||
| `list` | The array at `pointer`, its `fields`, `membership` (`all`: every entity of the collection in state order, when each recording listed exactly that, had no query or only paging parameters (`limit`, `offset`, `page`, `per_page`, `perpage`, `page_size`, `pagesize`, `maxsize`, `max_results`, `maxresults`, `size`, `start`, `_start`, `_end`, ignoring case), no item-total member (`total`, `count`, `totalCount`, `item_count`, `totalcount`, `itemcount`, `numberOfElements`, but not `total_pages` or `per_page`; a number or a string of digits; top-level, or in a top-level object beside the list such as `pageInfo.totalRows`) differing from the number listed, and no `has_more`, `next` or next-page token member set; `recorded`: the entities each recording listed, except on the lines `all_record_ordinals` names, which each meet those conditions on their own and list every entity), `counters` that equal the number listed, and `listed_ids`: top-level members that list two or more listed entities' identity values in list order (Paperless's `all`), which a rendered list sets to its own unless it is a page of more matches |
|
||||
| `document` | The latest initial value of a list tool whose responses hold no identified list, when its initial reads share one request line; held in `documents` with that `path`, whether or not the capture writes it, unless its value changed across any recorded write between those reads (`fields-differ`); its `per_response_members` are the members of the value each response on that path mints |
|
||||
| `recorded` | A `reason` the tool replays its recordings instead |
|
||||
|
||||
An item or list plan is kept only when it reproduces the latest initial
|
||||
recording of each request line from the state, comparing JSON values without
|
||||
@@ -712,7 +712,7 @@ exactly what it recorded from the collection's initial entities:
|
||||
| `search` | `q`, `query`, `search`, `searchTerm`, `term`, `keyword`(s) | entities with one of `fields` containing the value, ignoring case; the fields are those it matched in the listed items, URLs only where nothing else matched; a term that matched no field leaves the parameter inert until another line's term does |
|
||||
| `sort` | a name asking for an order in any common spelling: `sort`, `_sort`, `sort[]`, `sort[0][field]`, `sorters[...]`, `sortBy`, `sort-column`, `sortKey`, `order`, `orderBy`, `ordering` | ordered by the field the value names (`name`, `-name`, `name:desc`, `name-ASC`): numbers by value, text ignoring case, null last ascending, ties in state order |
|
||||
| `direction` | a direction name (`dir`, `direction`, `sortDir`, `sortOrder`, `sortDirection`, `order_direction`, json-server's `_order`, and `order`) given `asc`, `desc`, `ascending` or `descending`; given anything else, `order` is a `sort` and the others are ordinary parameters | the sort's direction |
|
||||
| `limit` | `limit`, `per_page`, `perPage`, `page_size`, `size`, `count`, `take`, `itemsPerPage` | page size; zero or a negative value lists every match |
|
||||
| `limit` | `limit`, `per_page`, `perPage`, `page_size`, `size`, `count`, `take`, `itemsPerPage`, `maxSize`, `max_results`, `maxResults` | page size; zero or a negative value lists every match |
|
||||
| `offset` | `offset`, `start`, `_start`, `skip` | matches skipped before the page |
|
||||
| `page` | `page` | page number, counted from `first` (1 or 0, whichever explains the line) |
|
||||
| `inert` | any that names no order or direction; never a true `reverse`, or a paging value that is not a safe integer | changes nothing for its recorded `values`; other values are not computed |
|
||||
@@ -733,8 +733,12 @@ order. Its `effect` is `create` for a `POST` to a type's
|
||||
plus one trailing parameter, or to that item path plus one action segment,
|
||||
`delete` for a `DELETE` to it, `document` for a `PUT`, `PATCH` or `POST` to a
|
||||
document's path, an `update` or `delete` of each entity a write at the
|
||||
collection names by id in its body, and otherwise `none` with reason
|
||||
`no-state-target`. A plan is built from the tool's first successful call: its
|
||||
collection names by id in its body, a `create` for a `POST` whose answer names
|
||||
an id (at its top level, in an object member or in the one object of a
|
||||
one-element array member) that a later item read of a type first shows (served
|
||||
only with the path values its recorded call sent, unless it creates into the
|
||||
type's own collection, whose path has no parameters, and a `path` rule stores
|
||||
each of its path values), and otherwise `none` with reason `no-state-target`. A plan is built from the tool's first successful call: its
|
||||
request body must be a usable JSON object (as response bodies are usable), and
|
||||
the written entity must show in that call's response or in a later read before
|
||||
the next write. Each field of a created entity gets the first rule that
|
||||
@@ -743,21 +747,38 @@ value's shape), `echo` of a body member (`sorted` when the application stored
|
||||
its strings in code-point order rather than as sent), `copy` of a body string of
|
||||
8 or more characters, a `path` value, a `slug` of a body member, a `clock` value within 10
|
||||
minutes of the request in its recorded format, a `sequence` one past the
|
||||
largest value of its collection or type, a `unique` UUID, opaque token or
|
||||
largest value of its collection or type, and never below its `start`, a `unique` UUID, opaque token or
|
||||
integer of 10 or more digits that no entity of the type holds (where every one
|
||||
holds a distinct integer of the same length, as time-based ids do), a
|
||||
`nested-clock` object whose clocks the request stamped, a `template` around
|
||||
`nested-clock` object whose clocks the request stamped, a `made-object` no
|
||||
other entity of the type holds otherwise, whose `clocks` the request stamped and
|
||||
whose other members hold the entity's own identity (`own`), one past the largest
|
||||
value held objects show (`counts`, each from a `start`), or what every held
|
||||
object shows, a `template` around
|
||||
a generated value, a `reference` to an entity, or a `composed` string; other
|
||||
fields keep the observed value. When the first read of a created entity before
|
||||
the next write shows fields the create's answer lacks, or its clocks to another
|
||||
precision, the observed entity takes them (`observation.read`). An item read
|
||||
the next write shows fields the create's answer lacks, its clocks to another
|
||||
precision, or values the answer had not loaded (an empty, zero or `false` value,
|
||||
Go's zero time, or an object whose members are each loaded so), the observed
|
||||
entity takes them (`observation.read`, whose `unloaded` points to the values the
|
||||
answer had not loaded). An item read
|
||||
that agrees with the answer otherwise lends its fields and clock spellings and
|
||||
no longer confirms the create; one that disagrees or omits an answer field its
|
||||
item view renders still checks it. Clock agreement uses the coarser recorded
|
||||
precision, including microseconds. A list read
|
||||
only adds fields the answer lacks and still checks it. Serving answers a create
|
||||
in its answer's shape and clock precision and stores the fields of both with
|
||||
the item read-back's clock precision. A `reference` is an object holding fields of the
|
||||
in its answer's shape and clock precision, with empty lists and maps the call
|
||||
did not send spelled (`null`, `[]` or `{}`) as its answer spelled them and the
|
||||
values it had not loaded as it answered them (unless the call sent one the plan
|
||||
stores as sent), leaving out members the read added inside an object the answer
|
||||
showed, and stores the fields of both,
|
||||
as the item read-back shows them, with its clock precision. Updates
|
||||
answer empty lists and maps they did not send as their answers spelled them
|
||||
too; a member a call sent is answered as sent. An update or delete that answers
|
||||
with the entity leaves out a field only later reads showed, which no initial
|
||||
entity holds, when the entity does not hold it, as Vikunja's reads of tasks it
|
||||
did not create show no `subscription`. A `reference` is an object
|
||||
holding fields of the
|
||||
entity a sent member names, as the state holds it (a parent beside `parentId`):
|
||||
the member is accepted with any value, serving looks the entity up and refuses a
|
||||
call naming one the environment does not hold or holds in more than one
|
||||
@@ -768,9 +789,21 @@ when, explained on its own, it agrees with it: both calls show the same fields
|
||||
but for a reference whose member a call without it did not send (one an earlier
|
||||
create added stays), every field both rule they rule alike (an echo one showed
|
||||
sorted stays sorted, unless a call stored its strings out of order as sent),
|
||||
and a field neither rules holds one value. Members either call sent are accepted, only those every call sent are
|
||||
and a field neither rules holds one value, unless the later call's rule for it
|
||||
spells the earlier calls' value too: a `composed` or `template` string, or a
|
||||
`sequence` started at that value (`start`), as Vikunja numbers a project's tasks
|
||||
past those deleted before the capture. Two `made-object` rules agree in their
|
||||
clocks and own members, and a count only the later call shows starts at the
|
||||
first object's value; a later create's made object must hold clocks its own
|
||||
request stamped, in the rule's format. Members either call sent are accepted, only those every call sent are
|
||||
required, and an echoed member a call left out takes the value that call stored
|
||||
(`body.defaults`). A clock rule widens to `trimmed` only when no held entity or
|
||||
(`body.defaults`). A member a create sent empty (`""`, `null`, `false`, `[]` or
|
||||
`{}`), as a form sends a field left as it was, is not required: a call that
|
||||
leaves it out is applied as sending that value (`body.blank`), unless a call
|
||||
that left it out showed a default, which it then keeps. A call may send it as any
|
||||
recorded call sent it empty, even after a default is learned, and one leaving it
|
||||
out is applied as the earliest did, as when one create sent `false` and another
|
||||
`null`. A clock rule widens to `trimmed` only when no held entity or
|
||||
earlier call showed that clock at a fixed width, and a slug to spelling `&` as
|
||||
the word (`ampersand: "and"`) only when no call the plan accepted nor entity the
|
||||
type held dropped one. A later
|
||||
@@ -783,7 +816,8 @@ or leaves out is `unexplained-change`. A create whose observed reference
|
||||
differs from the
|
||||
entity the plan rebuilds, whether or not it sent the reference member, is
|
||||
`unexplained-change`. So is a create that left out an echoed member yet shows a
|
||||
value different from the learned default.
|
||||
value different from the learned default, or that stored a field no rule writes
|
||||
otherwise than the plan keeps it.
|
||||
A `composed` string is literal text and parts read from the fields other rules
|
||||
write: a field's value, its slug, or a clock's calendar digits
|
||||
(`YYYYMMDDhhmmss`, `YYYYMMDD`, `YYYY-MM-DD` or `YYYY/MM/DD`, in the clock's
|
||||
@@ -821,7 +855,8 @@ a newly learned slug. Any other member stored
|
||||
differently is accepted only with its
|
||||
recorded value (`body.fixed`) and stores the value the call that showed it
|
||||
recorded (the `value` of its `constant` rule), and a field it changed without
|
||||
sending must be a clock, a nested clock, an increment, a slug, a fresh `unique`
|
||||
sending (an empty list or map one answer spells `null` and another `[]` or `{}`
|
||||
is unchanged) must be a clock, a nested clock, an increment, a slug, a fresh `unique`
|
||||
value, a `stamp` of the acting user, a value `derived` from the one member it
|
||||
stores nowhere, a `reference` to the entity a sent member names (kept by a call that names the entity the field already repeats), a `composed` string whose parts read members it stored as sent
|
||||
or fields it left as they were and spell the value before the call too, or for
|
||||
@@ -831,7 +866,7 @@ value or set must hold for every call of the tool, and an action is served
|
||||
only once a call found what it sets already set. A write is served only
|
||||
with the query its first call sent, which serving applies to a call that
|
||||
leaves a parameter out and the catalog advertises as required. Every member the first call sent is required, except by `PATCH` and
|
||||
`POST` updates.
|
||||
`POST` updates and members a create sent empty.
|
||||
|
||||
A read on a line `query` answers is checked against the query evaluated over
|
||||
the replayed state. It covers the collection only when every parameter sorts or
|
||||
@@ -846,8 +881,15 @@ collection when the path names no held entity), and taints what it wrote at or
|
||||
above. A read the served environment would answer from the state is compared
|
||||
with its recording as the read plans are, as the whole response it would serve:
|
||||
the entity or list rendered into the envelope of the item view's or the line's
|
||||
base recording, with the members the state still holds. A 400, 404 or 410 (or a
|
||||
`null` body) agrees with an entity the copy does not hold. A match confirms
|
||||
base recording, with the members the state still holds. A 400, 404 or 410, a
|
||||
`null` body, or an object marked deleted (`deleted`, `isDeleted` or `is_deleted`
|
||||
`true`, as EspoCRM answers a read of a deleted record) agrees with an entity the
|
||||
copy does not hold; such a read drops a held entity it names and adopts none,
|
||||
while an initial read keeps the record it shows held, as its item read answers it.
|
||||
The item view records the flag its reads set as `deleted_flag`, a field it shows
|
||||
even when only a query read showed it, which serving answers by: later reads of
|
||||
a served-deleted entity show it marked deleted, and a served delete answers with
|
||||
the flag as its recorded answer showed it. A match confirms
|
||||
the writes that changed its entity or collection since it was last read, less
|
||||
those whose entity a read since showed absent, which no longer count for the
|
||||
collection; a mismatch contradicts them, or, when an unmodelled write came after them, is
|
||||
@@ -855,11 +897,30 @@ checked only on the members they sent; a mismatch no write explains marks the re
|
||||
`side-effect`. A plan is `served` when a read confirmed it and none
|
||||
contradicted it; otherwise its `reason` is the first of its build reason
|
||||
(`no-call`, `body-unusable`, `no-observation`, `unexplained-change`),
|
||||
`contradicted` (with `contradicted_by`), `side-effect`, `unconfirmed` and `incomplete-entities` (an update or delete that answers with the entity, when no initial entity holds every field its recorded answer shows, counting for an update the fields its rules write, so it could answer only for entities the environment created). An otherwise served plan whose learned rules generate clock values is withheld with `unknown-clock` when no global clock start is established; its learned rules and confirming reads remain retained.
|
||||
`contradicted` (with `contradicted_by`), `side-effect`, `unconfirmed` and `incomplete-entities` (an update or delete that answers with the entity, when no initial entity holds every field its recorded answer shows but those only later reads showed, counting for an update the fields its rules write, so it could answer only for entities the environment created). An otherwise served plan whose learned rules generate clock values is withheld with `unknown-clock` when no global clock start is established; its learned rules and confirming reads remain retained.
|
||||
`checked_record_ordinals` lists the confirming reads, `insert` places created
|
||||
entities (sorted by a field the collection's recorded order shows, else last),
|
||||
and `write_clock` is the end of the last timed record, to the next second, when
|
||||
all request completions are known; otherwise it is null.
|
||||
all request completions are known; otherwise it is null. An
|
||||
update that writes the field its type's creates are sorted by moves its entity
|
||||
to where the new value sorts (its own `insert`); a bulk update moves each entity
|
||||
it writes in turn, as replay applies them. When a read listing the whole
|
||||
collection in its own order, not one its query sorts, fails after an update and the type's creates are not sorted by a field
|
||||
that update writes, the view tries the order serving gives when each write since
|
||||
a list in the collection's own order showed its entity moves it in turn by such a
|
||||
field, against the others' values before their own writes, when
|
||||
the entities no write has changed since a list in the collection's own order last
|
||||
showed them already follow it, as serving moves only the entities a write
|
||||
changes, and unless an earlier read listing the whole collection in its own
|
||||
order showed a write that changed such a field where moving it by that order
|
||||
would not have put it, a create was placed by a sort that excludes it, or a read listing the whole
|
||||
collection in its own order, not one its query sorts, that placed a create did
|
||||
not follow it; on a match, the type's creates
|
||||
and updates take that order, as Grafana keeps folders by title though no earlier
|
||||
read could tell that from creation order, or from id order where both ascend.
|
||||
An update moves its entity by its creates' sort only when a list read showed a
|
||||
create placed, in replay as in serving, so no read confirms an update by a sort
|
||||
a create only guessed from the initial order.
|
||||
|
||||
Values in the state are recorded data, so the view refuses the same parents as
|
||||
the manifest, including those inspected with `sensitive_values`, and a
|
||||
|
||||
@@ -2695,11 +2695,11 @@ [email protected] | runtime | MIT
|
||||
Package: https://www.npmjs.com/package/fast-string-width/v/3.0.2
|
||||
Archive: https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz
|
||||
Integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==
|
||||
[email protected].8 | runtime | BSD-3-Clause
|
||||
[email protected].9 | runtime | BSD-3-Clause
|
||||
Lockfile path: node_modules/fast-uri
|
||||
Package: https://www.npmjs.com/package/fast-uri/v/3.1.8
|
||||
Archive: https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz
|
||||
Integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==
|
||||
Package: https://www.npmjs.com/package/fast-uri/v/3.1.9
|
||||
Archive: https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz
|
||||
Integrity: sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==
|
||||
[email protected] | runtime | MIT
|
||||
Lockfile path: node_modules/fast-wrap-ansi
|
||||
Package: https://www.npmjs.com/package/fast-wrap-ansi/v/0.2.2
|
||||
@@ -3775,11 +3775,11 @@ [email protected] | development | MIT
|
||||
Package: https://www.npmjs.com/package/pure-rand/v/8.4.2
|
||||
Archive: https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.2.tgz
|
||||
Integrity: sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==
|
||||
[email protected].0 | development | BSD-3-Clause
|
||||
[email protected].1 | development | BSD-3-Clause
|
||||
Lockfile path: node_modules/qs
|
||||
Package: https://www.npmjs.com/package/qs/v/6.16.0
|
||||
Archive: https://registry.npmjs.org/qs/-/qs-6.16.0.tgz
|
||||
Integrity: sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==
|
||||
Package: https://www.npmjs.com/package/qs/v/6.16.1
|
||||
Archive: https://registry.npmjs.org/qs/-/qs-6.16.1.tgz
|
||||
Integrity: sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==
|
||||
[email protected] | runtime | MIT
|
||||
Lockfile path: node_modules/quick-format-unescaped
|
||||
Package: https://www.npmjs.com/package/quick-format-unescaped/v/4.0.4
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
REA third-party npm runtime copyright, license and NOTICE texts
|
||||
Collected from integrity-verified locked npm archives. Release-specific supplements and upstream notice gaps are identified in their entries.
|
||||
External engines are separately obtained; see THIRD_PARTY_NOTICES.md.
|
||||
Runtime dependency fingerprint (SHA-256): 31bd2b9c5690c9d963d7dcf25da2fec9bc16a3d4724f661cf92489dd106fdbe1
|
||||
Runtime dependency fingerprint (SHA-256): 09f8570cf168ddd3ad7c877dbcdd1ef5013de3af884a37fd27e6da3b99b49664
|
||||
|
||||
===== @babel/[email protected] =====
|
||||
Declared license: MIT
|
||||
@@ -3926,10 +3926,10 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
===== [email protected].8 =====
|
||||
===== [email protected].9 =====
|
||||
Declared license: BSD-3-Clause
|
||||
Archive: https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz
|
||||
Integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==
|
||||
Archive: https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz
|
||||
Integrity: sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==
|
||||
Scope: npm runtime dependency, including optional platform packages.
|
||||
|
||||
--- package/LICENSE ---
|
||||
|
||||
Generated
+6
-6
@@ -7776,9 +7776,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "3.1.8",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz",
|
||||
"integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==",
|
||||
"version": "3.1.9",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz",
|
||||
"integrity": "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -10729,9 +10729,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz",
|
||||
"integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.16.1.tgz",
|
||||
"integrity": "sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
|
||||
+8
-2
@@ -99,7 +99,13 @@
|
||||
"bridge/pwndbg/core_context.py",
|
||||
"third_party/pwndbg/README.md",
|
||||
"third_party/pwndbg/LICENSE.md",
|
||||
"licenses"
|
||||
"licenses",
|
||||
"assets/rea-plugin.png",
|
||||
".claude-plugin/plugin.json",
|
||||
".codex-plugin/plugin.json",
|
||||
".codex-plugin/mcp.json",
|
||||
"scripts/plugin-mcp-launcher.mjs",
|
||||
"npm-shrinkwrap.json"
|
||||
],
|
||||
"scripts": {
|
||||
"release:notes": "node scripts/release-notes.mjs",
|
||||
@@ -381,7 +387,7 @@
|
||||
},
|
||||
"overrides": {
|
||||
"typed-rest-client": {
|
||||
"qs": "6.16.0"
|
||||
"qs": "6.16.1"
|
||||
},
|
||||
"vitepress": {
|
||||
"vite": "6.4.4"
|
||||
|
||||
@@ -31,6 +31,36 @@
|
||||
"type": "json",
|
||||
"path": "server.json",
|
||||
"jsonpath": "$.packages[0].version"
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": ".claude-plugin/plugin.json",
|
||||
"jsonpath": "$.version"
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": ".claude-plugin/plugin.json",
|
||||
"jsonpath": "$.mcpServers.rea.args[1]"
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": ".codex-plugin/plugin.json",
|
||||
"jsonpath": "$.version"
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": ".codex-plugin/mcp.json",
|
||||
"jsonpath": "$.mcpServers.rea.args[1]"
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": ".claude-plugin/marketplace.json",
|
||||
"jsonpath": "$.plugins[0].source.version"
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": ".agents/plugins/marketplace.json",
|
||||
"jsonpath": "$.plugins[0].source.version"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+10
-1
@@ -1,7 +1,12 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { appendFileSync } from "node:fs";
|
||||
import { matchesGlob } from "node:path";
|
||||
import { CI_FLAGS as flags, MANUAL_VERIFIERS, SCOPES } from "./scopes.mjs";
|
||||
import {
|
||||
CI_FLAGS as flags,
|
||||
MANUAL_VERIFIERS,
|
||||
SCOPES,
|
||||
WINDOWS_EXECUTABLE_SELECTION_INPUTS,
|
||||
} from "./scopes.mjs";
|
||||
import { planTestSelection } from "./test-selection.mjs";
|
||||
|
||||
// Test files select the host/runtime lanes that execute their focused suites.
|
||||
@@ -206,6 +211,10 @@ if (tier === "reused") {
|
||||
.split("\0")
|
||||
.filter(Boolean);
|
||||
for (const file of files) {
|
||||
selected.package ||= matchesAny(
|
||||
file,
|
||||
WINDOWS_EXECUTABLE_SELECTION_INPUTS,
|
||||
);
|
||||
if (matchesAny(file, SCOPES.reverse_tools)) selected.reverse_tools = true;
|
||||
if (
|
||||
/^(README(?:_[^/]+)?\.md|AGENTS\.md|CONTRIBUTING\.md|CHANGELOG\.md|SECURITY\.md|native\/windows\/README\.md|\.github\/pull_request_template\.md)$/.test(
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
// These boundaries require native Windows launch verification even where
|
||||
// broad process ownership would otherwise suppress host fanout on the fast tier.
|
||||
export const WINDOWS_EXECUTABLE_SELECTION_INPUTS = [
|
||||
"src/process/WindowsExecutable*.ts",
|
||||
"src/adb/AdbConfiguration.ts",
|
||||
"src/apktool/ApktoolConfiguration.ts",
|
||||
"src/application/UpdateCommandRunner.ts",
|
||||
"tests/boundary/process/windowsExecutableSelection.test.ts",
|
||||
];
|
||||
|
||||
// Ownership of the native-host, real-provider and website verification lanes.
|
||||
// Shared build inputs are classified once in plan.mjs rather than repeated in
|
||||
// every workflow.
|
||||
@@ -67,6 +77,13 @@ export const SCOPES = {
|
||||
],
|
||||
// Apple artifact CLI/MCP lane on macOS, including its focused regressions.
|
||||
apple: [
|
||||
"src/hopper/BridgeLauncher.ts",
|
||||
"src/domain/hopperErrors.ts",
|
||||
"tests/boundary/providers/hopper/hopperMacLaunchArguments.test.ts",
|
||||
"tests/acceptance/providers/hopperStartupRecovery.test.ts",
|
||||
"tests/boundary/providers/hopper/hopperClient.helperFailure.test.ts",
|
||||
"tests/boundary/providers/hopper/bridgeBootstrapCleanup.test.ts",
|
||||
"tests/boundary/providers/hopper/bridgeTargetLease.integration.test.ts",
|
||||
"src/artifacts/NativeDmg*.ts",
|
||||
"src/native/Apple*",
|
||||
"src/native/NativeMacOS*",
|
||||
@@ -101,6 +118,7 @@ export const SCOPES = {
|
||||
],
|
||||
// Four-host installed-package matrix and the Windows curated lane.
|
||||
package: [
|
||||
...WINDOWS_EXECUTABLE_SELECTION_INPUTS,
|
||||
// Both host compilers must be exercised when their installation, options,
|
||||
// or entrypoints change; Linux's accelerated checker cannot stand in for Go.
|
||||
"scripts/run-typescript.*",
|
||||
|
||||
@@ -91,7 +91,8 @@ try {
|
||||
typed
|
||||
? {
|
||||
...process.env,
|
||||
GOMAXPROCS: process.env.GOMAXPROCS ?? "2",
|
||||
// One worker reuses its checker caches across the full file queue.
|
||||
GOMAXPROCS: process.env.GOMAXPROCS ?? "1",
|
||||
GOMEMLIMIT: process.env.GOMEMLIMIT ?? "1024MiB",
|
||||
}
|
||||
: process.env,
|
||||
|
||||
@@ -66,6 +66,7 @@ class Generator:
|
||||
first.request.path = "http://user:[email protected]/a?token=ordinary#fragment"
|
||||
first.response = http.Response.make(200, b"\x00\xfeanswer", [(b"Set-Cookie", b"session=cookie-secret"), (b"Location", b"https://redirect-user:[email protected]/b?token=ordinary")])
|
||||
first.metadata = {"big_integer": 9007199254740993, "nonfinite": float("inf"), "isLosslessNumber": True, "value": "ordinary-extension", "response": {"headers": [["Authorization", "ordinary-extension-credential-name"]]}}
|
||||
first.metadata["numeric_spellings"] = {"small": 1e-7, "large": 1e20, "ordinary": 42}
|
||||
first.websocket = WebSocketData(messages=[WebSocketMessage(2, True, b"\x00\xfdmessage", 123.5)])
|
||||
first.backup()
|
||||
first.comment = "caller-selected-fixture"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Exercised runtime graph: CPython 3.14.7, Linux x86_64 guest.
|
||||
# Base runtime graph: CPython 3.14.7, Linux x86_64 guest.
|
||||
# The certifi trust-store refresh has not been rerun in that guest.
|
||||
aioquic==1.2.0
|
||||
attrs==26.1.0
|
||||
certifi==2026.7.22
|
||||
certifi==2026.10.19
|
||||
cffi==2.1.1
|
||||
cryptography==50.0.2
|
||||
pycparser==3.11
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
interface PackageManifest {
|
||||
name: string;
|
||||
version: string;
|
||||
devDependencies?: Record<string, string>;
|
||||
dependencies?: Record<string, string>;
|
||||
overrides?: Record<string, unknown>;
|
||||
// npm manifests are extensible; preserve metadata outside this projection.
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface LockedPackage {
|
||||
version?: string;
|
||||
dev?: boolean;
|
||||
// Keep unmodified integrity, resolution, platform, and other lock-entry facts.
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface PackageLock {
|
||||
name: string;
|
||||
version: string;
|
||||
lockfileVersion: number;
|
||||
packages: Record<string, LockedPackage>;
|
||||
// Preserve other npm lock metadata rather than dropping unfamiliar fields.
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export function pluginPackageMetadata<Manifest extends PackageManifest>(
|
||||
manifest: Manifest,
|
||||
lock: PackageLock,
|
||||
): {
|
||||
packageJson: Omit<Manifest, "devDependencies" | "overrides"> & {
|
||||
bundleDependencies: string[];
|
||||
};
|
||||
shrinkwrap: PackageLock;
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/** Project the development manifest and lock into the shared runtime artifact. */
|
||||
export function pluginPackageMetadata(manifest, lock) {
|
||||
if (
|
||||
lock.lockfileVersion !== 3 ||
|
||||
lock.name !== manifest.name ||
|
||||
lock.version !== manifest.version ||
|
||||
lock.packages?.[""]?.version !== manifest.version
|
||||
)
|
||||
throw new Error("Plugin packaging requires the matching v3 package lock");
|
||||
|
||||
// Claude skips dependency installation when a published manifest has
|
||||
// overrides. Preserve development security overrides in the source owner,
|
||||
// and reject any future runtime override rather than silently discarding it.
|
||||
for (const name of Object.keys(manifest.overrides ?? {})) {
|
||||
const matches = Object.entries(lock.packages).filter(([path]) =>
|
||||
path.endsWith(`node_modules/${name}`),
|
||||
);
|
||||
if (matches.length === 0 || matches.some(([, entry]) => entry.dev !== true))
|
||||
throw new Error(`Cannot omit a runtime or unresolved override: ${name}`);
|
||||
}
|
||||
|
||||
const packageJson = { ...manifest };
|
||||
delete packageJson.devDependencies;
|
||||
delete packageJson.overrides;
|
||||
// Codex's npm source fetches the archive without installing dependencies.
|
||||
// Keep optional Frida outside the bundle so ordinary npm installs can still
|
||||
// run its host-specific installer. PTY binaries are transitive optionals of
|
||||
// a runtime dependency and are included for every supported platform by the
|
||||
// archive producer.
|
||||
packageJson.bundleDependencies = Object.keys(manifest.dependencies ?? {});
|
||||
const root = { ...lock.packages[""] };
|
||||
delete root.devDependencies;
|
||||
const shrinkwrap = {
|
||||
...lock,
|
||||
packages: Object.fromEntries(
|
||||
Object.entries(lock.packages).filter(([, entry]) => entry.dev !== true),
|
||||
),
|
||||
};
|
||||
shrinkwrap.packages[""] = root;
|
||||
return { packageJson, shrinkwrap };
|
||||
}
|
||||
@@ -4,6 +4,37 @@ import { z } from "zod";
|
||||
const shaSchema = z.string().regex(/^[a-f0-9]{40}$/u);
|
||||
const versionSchema = z.object({ version: z.string() });
|
||||
const manifestSchema = z.object({ ".": z.string() });
|
||||
const PLUGIN_MANIFEST_PATHS = [
|
||||
".claude-plugin/plugin.json",
|
||||
".codex-plugin/plugin.json",
|
||||
];
|
||||
const inlinePluginManifestSchema = versionSchema.extend({
|
||||
mcpServers: z.object({
|
||||
rea: z.object({
|
||||
command: z.literal("node"),
|
||||
args: z.tuple([
|
||||
z.literal("${CLAUDE_PLUGIN_ROOT}/scripts/plugin-mcp-launcher.mjs"),
|
||||
z.string(),
|
||||
]),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
const codexPluginManifestSchema = versionSchema.extend({
|
||||
mcpServers: z.literal("./.codex-plugin/mcp.json"),
|
||||
});
|
||||
const codexServerConfigSchema = z.object({
|
||||
mcpServers: z.object({
|
||||
rea: z.object({
|
||||
command: z.literal("node"),
|
||||
args: z.tuple([
|
||||
z.literal("./scripts/plugin-mcp-launcher.mjs"),
|
||||
z.string(),
|
||||
]),
|
||||
cwd: z.literal("."),
|
||||
startup_timeout_sec: z.number().int().positive(),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
const sectionsSchema = z.object({
|
||||
versioning: z.string().optional(),
|
||||
"changelog-sections": z.array(
|
||||
@@ -189,6 +220,26 @@ async function validateCandidate(git, sha, version, commits) {
|
||||
["server.json", server.version],
|
||||
["server.json rea-agents package", npmPackage.version],
|
||||
);
|
||||
// A stale manifest keeps the previous plugin cache key or launches the
|
||||
// previous server, so both are version authority like server.json.
|
||||
const launches = [];
|
||||
for (const path of PLUGIN_MANIFEST_PATHS) {
|
||||
const manifest =
|
||||
path === ".codex-plugin/plugin.json"
|
||||
? await readJson(git, sha, path, codexPluginManifestSchema)
|
||||
: await readJson(git, sha, path, inlinePluginManifestSchema);
|
||||
versions.push([path, manifest.version]);
|
||||
const server =
|
||||
path === ".codex-plugin/plugin.json"
|
||||
? await readJson(
|
||||
git,
|
||||
sha,
|
||||
".codex-plugin/mcp.json",
|
||||
codexServerConfigSchema,
|
||||
)
|
||||
: manifest;
|
||||
launches.push([path, server.mcpServers.rea.args[1]]);
|
||||
}
|
||||
// Catalogs are generated and checked from this candidate by the release job;
|
||||
// they are not version authority stored in the immutable source tree.
|
||||
const generated = await git([
|
||||
@@ -210,6 +261,14 @@ async function validateCandidate(git, sha, version, commits) {
|
||||
);
|
||||
}
|
||||
}
|
||||
// The launcher checks the scalar pin against the runtime in its own package.
|
||||
for (const [location, launched] of launches) {
|
||||
if (launched !== version) {
|
||||
throw new Error(
|
||||
`${location}@${sha} launches version ${launched ?? "no version"}; release branch requires ${version}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
const notes = releaseNotes(
|
||||
await git(["show", `${sha}:CHANGELOG.md`]),
|
||||
version,
|
||||
|
||||
@@ -369,6 +369,60 @@ export function verifyReleaseNotes(raw, text, body) {
|
||||
};
|
||||
}
|
||||
|
||||
const VERSION_ONLY_PLUGIN_METADATA = new Set([
|
||||
".claude-plugin/plugin.json",
|
||||
".codex-plugin/plugin.json",
|
||||
".codex-plugin/mcp.json",
|
||||
".claude-plugin/marketplace.json",
|
||||
".agents/plugins/marketplace.json",
|
||||
]);
|
||||
|
||||
/** Serialize plugin metadata with only its release-owned version fields blanked. */
|
||||
function pluginMetadataWithoutVersions(path, text) {
|
||||
const manifest = JSON.parse(text);
|
||||
if (manifest === null || typeof manifest !== "object") return text;
|
||||
if (path.endsWith("marketplace.json")) {
|
||||
if (Array.isArray(manifest.plugins))
|
||||
for (const plugin of manifest.plugins)
|
||||
if (
|
||||
plugin?.name === "rea" &&
|
||||
plugin?.source?.source === "npm" &&
|
||||
plugin.source.package === "rea-agents" &&
|
||||
typeof plugin.source.version === "string"
|
||||
)
|
||||
plugin.source.version = "";
|
||||
return JSON.stringify(manifest);
|
||||
}
|
||||
if ("version" in manifest) manifest.version = "";
|
||||
const launch = manifest.mcpServers?.rea?.args;
|
||||
// Only the scalar pinned version is release-owned; the launcher owns the
|
||||
// fixed package name and the executable/path remain product behavior.
|
||||
if (Array.isArray(launch) && typeof launch[1] === "string") launch[1] = "";
|
||||
return JSON.stringify(manifest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin metadata also carries behavior, so a release may change only
|
||||
* its version, pinned server launch, and exact REA marketplace version. Any other difference, an unreadable
|
||||
* side, or a manifest added by the release counts as a product change.
|
||||
*/
|
||||
async function changesOnlyPluginVersions(run, path, from, to) {
|
||||
try {
|
||||
return (
|
||||
pluginMetadataWithoutVersions(
|
||||
path,
|
||||
await run("git", ["show", `${from}:${path}`]),
|
||||
) ===
|
||||
pluginMetadataWithoutVersions(
|
||||
path,
|
||||
await run("git", ["show", `${to}:${path}`]),
|
||||
)
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Reject stale inventories and product changes made after the editorial target. */
|
||||
export async function verifyInventoryHistory(run, raw, source) {
|
||||
const inventory = releaseInventorySchema.parse(raw);
|
||||
@@ -437,7 +491,16 @@ export async function verifyInventoryHistory(run, raw, source) {
|
||||
)
|
||||
.split("\0")
|
||||
.filter(Boolean);
|
||||
const product = paths.filter((path) => !allowed.has(path));
|
||||
const product = [];
|
||||
for (const path of paths) {
|
||||
if (allowed.has(path)) continue;
|
||||
if (
|
||||
VERSION_ONLY_PLUGIN_METADATA.has(path) &&
|
||||
(await changesOnlyPluginVersions(run, path, inventory.target, resolved))
|
||||
)
|
||||
continue;
|
||||
product.push(path);
|
||||
}
|
||||
if (product.length)
|
||||
throw new Error(
|
||||
`Product changed after notes target; regenerate inventory: ${product.join(", ")}`,
|
||||
|
||||
@@ -36,9 +36,16 @@ export const verifyCompleteToolCatalog = async (client, options) => {
|
||||
);
|
||||
}
|
||||
}
|
||||
const sessionDefinition = listed.tools.find(
|
||||
({ name }) => name === "binary_session",
|
||||
);
|
||||
if (sessionDefinition === undefined)
|
||||
throw new Error("packaged MCP omitted the binary_session definition");
|
||||
const status = await client.callTool(
|
||||
{ name: "binary_session", arguments: {} },
|
||||
options,
|
||||
// The SDK otherwise compiles every listed output schema on the first call.
|
||||
// Keep its default validation for the actual named tool being exercised.
|
||||
{ ...options, toolDefinition: sessionDefinition },
|
||||
);
|
||||
const expanded = requireMcpLifecycleResult(status, "binary_session");
|
||||
const availability = expanded.tool_availability;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { writeFileSync } from "node:fs";
|
||||
|
||||
// Keep argument boundaries intact so verifier consumers never parse shell display text.
|
||||
writeFileSync(
|
||||
process.env.REA_NPX_LOG,
|
||||
JSON.stringify(process.argv.slice(2)) + "\n",
|
||||
);
|
||||
process.stdout.write("│ ✓ Cursor: isolated │\n");
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { access, readFile } from "node:fs/promises";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
// Release Please owns the scalar pin in each client declaration. The runtime
|
||||
// and skill come from the same installed artifact, without another npx child.
|
||||
const [version, ...extra] = process.argv.slice(2);
|
||||
try {
|
||||
if (
|
||||
extra.length !== 0 ||
|
||||
!/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/u.test(version ?? "")
|
||||
)
|
||||
throw new Error("requires one exact rea-agents@VERSION pin");
|
||||
|
||||
const bundledPackage = JSON.parse(
|
||||
await readFile(new URL("../package.json", import.meta.url), "utf8"),
|
||||
);
|
||||
if (
|
||||
bundledPackage.name !== "rea-agents" ||
|
||||
bundledPackage.version !== version
|
||||
)
|
||||
throw new Error(
|
||||
"version pin does not match its installed rea-agents package; reinstall the plugin",
|
||||
);
|
||||
|
||||
await access(new URL("../dist/mcp/main.js", import.meta.url)).catch(
|
||||
(cause) => {
|
||||
if (cause?.code !== "ENOENT") throw cause;
|
||||
throw new Error(
|
||||
"runtime is missing; reinstall the plugin, or run npm ci && npm run build:cached in a source checkout",
|
||||
{ cause },
|
||||
);
|
||||
},
|
||||
);
|
||||
} catch (cause) {
|
||||
process.stderr.write(`REA plugin ${cause.message}.\n`);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
||||
if (process.exitCode !== 1) {
|
||||
process.argv = [
|
||||
process.execPath,
|
||||
fileURLToPath(new URL("./rea.mjs", import.meta.url)),
|
||||
"mcp",
|
||||
];
|
||||
await import("./rea.mjs");
|
||||
}
|
||||
+28
-9
@@ -68,15 +68,34 @@ if (!(await compiledRuntimeExists(runtimeFiles))) {
|
||||
await runMcpHttpEntrypoint(args.slice(1));
|
||||
} else if (isMcpDoctorMode) {
|
||||
guardClosedStdout();
|
||||
const { runProductionMcpDoctorCli } =
|
||||
await import("../dist/cli-runtime/mcpDoctor.js");
|
||||
const result = await runProductionMcpDoctorCli(doctorArguments, {
|
||||
dispatcherPath: fileURLToPath(import.meta.url),
|
||||
packageRoot,
|
||||
stdoutIsTerminal: process.stdout.isTTY === true,
|
||||
});
|
||||
writeStdout(result.output);
|
||||
process.exitCode = result.exitCode;
|
||||
// Doctor runs outside Incur, so handle its global timing flag here. Keep a
|
||||
// token consumed by --format as a value, even when it looks like a flag.
|
||||
const outputValues = new Set(
|
||||
builtinArguments
|
||||
.splitIncurBuiltinArguments(doctorArguments)
|
||||
.outputValues.map(({ index }) => index),
|
||||
);
|
||||
const doctorCliArguments = doctorArguments.filter(
|
||||
(argument, index) => argument !== "--timings" || outputValues.has(index),
|
||||
);
|
||||
const timingRequested = doctorCliArguments.length !== doctorArguments.length;
|
||||
const started = performance.now();
|
||||
try {
|
||||
const { runProductionMcpDoctorCli } =
|
||||
await import("../dist/cli-runtime/mcpDoctor.js");
|
||||
const result = await runProductionMcpDoctorCli(doctorCliArguments, {
|
||||
dispatcherPath: fileURLToPath(import.meta.url),
|
||||
packageRoot,
|
||||
stdoutIsTerminal: process.stdout.isTTY === true,
|
||||
});
|
||||
writeStdout(result.output);
|
||||
process.exitCode = result.exitCode;
|
||||
} finally {
|
||||
if (timingRequested)
|
||||
process.stderr.write(
|
||||
`${JSON.stringify({ rea_timings: { version: 1, command_ms: Math.round(performance.now() - started) } })}\n`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const {
|
||||
cliConfigurationError,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { chmod, mkdir, symlink, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import { supportedClients } from "../dist/application/SupportedClients.js";
|
||||
|
||||
/** Create an isolated prefix, fake PATH, client configs, and environment. */
|
||||
@@ -41,7 +41,7 @@ export async function verifyPackageEnvironment({
|
||||
await writeFile(swVers, "#!/bin/sh\necho 14.5\n");
|
||||
await writeFile(
|
||||
npx,
|
||||
'#!/bin/sh\nprintf "%s\\n" "$*" > "$REA_NPX_LOG"\necho "│ ✓ Cursor: isolated │"\n',
|
||||
'#!/bin/sh\nexec "$REA_VERIFY_NODE" "$REA_VERIFY_NPX_SCRIPT" "$@"\n',
|
||||
);
|
||||
await writeFile(hopper, "#!/bin/sh\nexit 0\n");
|
||||
await Promise.all([
|
||||
@@ -98,6 +98,10 @@ export async function verifyPackageEnvironment({
|
||||
REA_ANALYSIS_PROVIDER: "auto",
|
||||
...packageHopperEnvironment(root),
|
||||
REA_NPX_LOG: npxLog,
|
||||
REA_VERIFY_NODE: process.execPath,
|
||||
REA_VERIFY_NPX_SCRIPT: fileURLToPath(
|
||||
new URL("./lib/verify-package-runner-fixture.mjs", import.meta.url),
|
||||
),
|
||||
REA_REFERENCE_SECRET_PATTERNS_JSON: JSON.stringify([".env"]),
|
||||
};
|
||||
return {
|
||||
|
||||
@@ -5,11 +5,12 @@ import {
|
||||
requireMcpToolError,
|
||||
} from "./lib/mcp-verifier-results.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import { join } from "node:path";
|
||||
import { readFile, rm } from "node:fs/promises";
|
||||
import { dirname, join } from "node:path";
|
||||
import { readFile, realpath, rm } from "node:fs/promises";
|
||||
|
||||
import { Client } from "@modelcontextprotocol/client";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
import { PRODUCT_IDENTITY } from "../dist/identity.js";
|
||||
import { resolveMcpStartupBudgets } from "../dist/config/mcpStartupBudgets.js";
|
||||
import { parseEvidence } from "../dist/domain/evidence.js";
|
||||
import * as prompts from "./verify-package-prompts.mjs";
|
||||
@@ -239,7 +240,7 @@ const verifyMcpInlineArtifactEvidence = async (
|
||||
}
|
||||
};
|
||||
|
||||
/** Connect to the packaged MCP server and verify catalog and Evidence composition. */
|
||||
/** Check CLI diagnostics, then verify catalog and Evidence through the installed plugin. */
|
||||
export async function verifyPackageMcp({
|
||||
commands,
|
||||
cli,
|
||||
@@ -249,7 +250,7 @@ export async function verifyPackageMcp({
|
||||
}) {
|
||||
const diagnosed = JSON.parse(
|
||||
(
|
||||
await commands.exec(cli, ["mcp", "doctor", "--json"], {
|
||||
await commands.exec(process.execPath, [cli, "mcp", "doctor", "--json"], {
|
||||
env: environment,
|
||||
timeout: resolveMcpStartupBudgets(environment).doctor.milliseconds,
|
||||
})
|
||||
@@ -261,9 +262,13 @@ export async function verifyPackageMcp({
|
||||
diagnosed.inventory?.tools?.expected
|
||||
)
|
||||
throw new Error("packaged production MCP doctor failed");
|
||||
const scriptsDirectory = dirname(await realpath(cli));
|
||||
const transport = new StdioClientTransport({
|
||||
command: cli,
|
||||
args: ["mcp"],
|
||||
command: process.execPath,
|
||||
args: [
|
||||
join(scriptsDirectory, "plugin-mcp-launcher.mjs"),
|
||||
PRODUCT_IDENTITY.packageVersion,
|
||||
],
|
||||
env: {
|
||||
...environment,
|
||||
},
|
||||
|
||||
+209
-10
@@ -1,11 +1,12 @@
|
||||
import { importVerifierSource } from "./lib/verifier-source-loader.mjs";
|
||||
|
||||
import { mkdir } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { access, copyFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
||||
import { FULL_CATALOG_IDENTITY } from "../dist/catalogIdentity.js";
|
||||
|
||||
import { parseTarListing } from "./lib/verify-package-listing.mjs";
|
||||
import { verifyPackedBridge } from "./verify-packed-bridge.mjs";
|
||||
import { pluginPackageMetadata } from "./lib/plugin-package.mjs";
|
||||
|
||||
const { parseSkillManifestMetadata } = await importVerifierSource(
|
||||
new URL("../src/domain/skillManifest.ts", import.meta.url).href,
|
||||
@@ -16,13 +17,108 @@ const { parseSkillManifestMetadata } = await importVerifierSource(
|
||||
export async function verifyPackagePack({ root, workspace, commands }) {
|
||||
const packDirectory = join(workspace, "pack");
|
||||
await mkdir(packDirectory);
|
||||
const stagingDirectory = join(workspace, "plugin-package");
|
||||
await mkdir(stagingDirectory);
|
||||
const [{ files }] = JSON.parse(
|
||||
(await commands.execNpm(["pack", "--dry-run", "--json"], { cwd: root }))
|
||||
.stdout,
|
||||
);
|
||||
// Reuse npm's file selection, then project metadata in an owned directory.
|
||||
// Source files and the development lock are never rewritten by packaging.
|
||||
for (const { path } of files) {
|
||||
const source = resolve(root, path);
|
||||
const contained = relative(root, source);
|
||||
if (
|
||||
contained === ".." ||
|
||||
contained.startsWith(`..${sep}`) ||
|
||||
isAbsolute(contained)
|
||||
)
|
||||
throw new Error(`npm selected a path outside the package: ${path}`);
|
||||
const destination = join(stagingDirectory, contained);
|
||||
await mkdir(dirname(destination), { recursive: true });
|
||||
await copyFile(source, destination);
|
||||
}
|
||||
const { packageJson, shrinkwrap } = pluginPackageMetadata(
|
||||
JSON.parse(await readFile(join(root, "package.json"), "utf8")),
|
||||
JSON.parse(await readFile(join(root, "package-lock.json"), "utf8")),
|
||||
);
|
||||
await writeFile(
|
||||
join(stagingDirectory, "package.json"),
|
||||
`${JSON.stringify(packageJson, null, 2)}\n`,
|
||||
);
|
||||
await writeFile(
|
||||
join(stagingDirectory, "npm-shrinkwrap.json"),
|
||||
`${JSON.stringify(shrinkwrap, null, 2)}\n`,
|
||||
);
|
||||
// Materialize exactly the locked runtime tree without executing installers.
|
||||
await commands.execNpm(
|
||||
["ci", "--omit=dev", "--ignore-scripts", "--no-audit", "--no-fund"],
|
||||
{ cwd: stagingDirectory },
|
||||
);
|
||||
if (packageJson.dependencies?.["@lydell/node-pty"] !== undefined) {
|
||||
// npm ci filters foreign-platform optional packages. Include the exact locked
|
||||
// PTY archives as well, without re-resolving the installed dependency tree.
|
||||
const ptyManifest = JSON.parse(
|
||||
await readFile(
|
||||
join(stagingDirectory, "node_modules/@lydell/node-pty/package.json"),
|
||||
"utf8",
|
||||
),
|
||||
);
|
||||
const platformDirectory = join(workspace, "plugin-platform-packages");
|
||||
await mkdir(platformDirectory);
|
||||
for (const dependency of Object.keys(ptyManifest.optionalDependencies)) {
|
||||
const destination = join(stagingDirectory, "node_modules", dependency);
|
||||
if (
|
||||
await access(destination).then(
|
||||
() => true,
|
||||
(cause) => {
|
||||
if (cause.code !== "ENOENT") throw cause;
|
||||
return false;
|
||||
},
|
||||
)
|
||||
)
|
||||
continue;
|
||||
const locked = shrinkwrap.packages[`node_modules/${dependency}`];
|
||||
if (locked?.version === undefined || locked.integrity === undefined)
|
||||
throw new Error(
|
||||
`Plugin platform dependency is not locked: ${dependency}`,
|
||||
);
|
||||
const [archive] = JSON.parse(
|
||||
(
|
||||
await commands.execNpm(
|
||||
[
|
||||
"pack",
|
||||
"--ignore-scripts",
|
||||
"--json",
|
||||
"--pack-destination",
|
||||
platformDirectory,
|
||||
`${dependency}@${locked.version}`,
|
||||
],
|
||||
{ cwd: platformDirectory },
|
||||
)
|
||||
).stdout,
|
||||
);
|
||||
if (archive.integrity !== locked.integrity)
|
||||
throw new Error(
|
||||
`Plugin platform archive differs from the lock: ${dependency}`,
|
||||
);
|
||||
await mkdir(destination, { recursive: true });
|
||||
await commands.exec("tar", [
|
||||
"-xzf",
|
||||
join(platformDirectory, archive.filename),
|
||||
"-C",
|
||||
destination,
|
||||
"--strip-components=1",
|
||||
]);
|
||||
}
|
||||
}
|
||||
const tarball = join(
|
||||
packDirectory,
|
||||
(
|
||||
await commands.execNpm(
|
||||
["pack", "--silent", "--pack-destination", packDirectory],
|
||||
{
|
||||
cwd: root,
|
||||
cwd: stagingDirectory,
|
||||
},
|
||||
)
|
||||
).stdout.trim(),
|
||||
@@ -34,6 +130,54 @@ export async function verifyPackagePack({ root, workspace, commands }) {
|
||||
(await commands.exec("tar", ["-xOf", tarball, "package/package.json"]))
|
||||
.stdout,
|
||||
);
|
||||
if (packedManifest.mcpName !== "io.github.morluto/rea")
|
||||
throw new Error("package did not retain its MCP Registry ownership marker");
|
||||
const packedLock = JSON.parse(
|
||||
(
|
||||
await commands.exec("tar", [
|
||||
"-xOf",
|
||||
tarball,
|
||||
"package/npm-shrinkwrap.json",
|
||||
])
|
||||
).stdout,
|
||||
);
|
||||
if (
|
||||
packedManifest.overrides !== undefined ||
|
||||
packedManifest.devDependencies !== undefined ||
|
||||
![2, 3].includes(packedLock.lockfileVersion) ||
|
||||
packedLock.version !== packedManifest.version ||
|
||||
packedLock.packages?.[""].version !== packedManifest.version ||
|
||||
JSON.stringify(packedLock.packages?.[""].dependencies) !==
|
||||
JSON.stringify(packedManifest.dependencies) ||
|
||||
JSON.stringify(packedLock.packages?.[""].optionalDependencies) !==
|
||||
JSON.stringify(packedManifest.optionalDependencies)
|
||||
)
|
||||
throw new Error(
|
||||
"package is not installable under the Claude plugin dependency policy",
|
||||
);
|
||||
for (const dependency of Object.keys(packedManifest.dependencies))
|
||||
if (
|
||||
!packedFiles.includes(`package/node_modules/${dependency}/package.json`)
|
||||
)
|
||||
throw new Error(
|
||||
`Plugin runtime dependency is not bundled: ${dependency}`,
|
||||
);
|
||||
const pty = JSON.parse(
|
||||
(
|
||||
await commands.exec("tar", [
|
||||
"-xOf",
|
||||
tarball,
|
||||
"package/node_modules/@lydell/node-pty/package.json",
|
||||
])
|
||||
).stdout,
|
||||
);
|
||||
for (const dependency of Object.keys(pty.optionalDependencies))
|
||||
if (
|
||||
!packedFiles.includes(`package/node_modules/${dependency}/package.json`)
|
||||
)
|
||||
throw new Error(
|
||||
`Plugin PTY platform dependency is missing: ${dependency}`,
|
||||
);
|
||||
if (
|
||||
packedManifest.scripts?.postinstall !== undefined ||
|
||||
packedManifest.dependencies?.["node-pty"] !== undefined ||
|
||||
@@ -45,14 +189,16 @@ export async function verifyPackagePack({ root, workspace, commands }) {
|
||||
packedManifest.bin?.["rea-agents"] !== "scripts/rea.mjs"
|
||||
)
|
||||
throw new Error("package did not expose both rea command entry points");
|
||||
if (packedManifest.mcpName !== "io.github.morluto/rea")
|
||||
throw new Error("package did not retain its MCP Registry ownership marker");
|
||||
if (!packedFiles.includes("package/scripts/lib/cli-process-environment.mjs"))
|
||||
throw new Error("package omitted the CLI process environment owner");
|
||||
if (packedFiles.some((path) => path.startsWith("package/.agents/")))
|
||||
throw new Error(
|
||||
"package included authored skill sources instead of only the generated bundle",
|
||||
);
|
||||
if (packedFiles.some((path) => path.startsWith("package/src/")))
|
||||
throw new Error(
|
||||
"Packaged runtime must not masquerade as a source checkout",
|
||||
);
|
||||
if (packedFiles.some((path) => path.startsWith("package/.cache/")))
|
||||
throw new Error("package included development cache or test metadata");
|
||||
const skill = (
|
||||
@@ -71,13 +217,66 @@ export async function verifyPackagePack({ root, workspace, commands }) {
|
||||
throw new Error(
|
||||
"packaged skill metadata did not match the catalog shipped in the package",
|
||||
);
|
||||
if (
|
||||
packedFiles.some(
|
||||
(path) => path.includes("__pycache__") || path.endsWith(".pyc"),
|
||||
for (const path of [
|
||||
".claude-plugin/plugin.json",
|
||||
".codex-plugin/plugin.json",
|
||||
]) {
|
||||
const manifest = JSON.parse(
|
||||
(await commands.exec("tar", ["-xOf", tarball, `package/${path}`])).stdout,
|
||||
);
|
||||
const serverConfig = path.startsWith(".codex")
|
||||
? JSON.parse(
|
||||
(
|
||||
await commands.exec("tar", [
|
||||
"-xOf",
|
||||
tarball,
|
||||
"package/.codex-plugin/mcp.json",
|
||||
])
|
||||
).stdout,
|
||||
)
|
||||
: manifest;
|
||||
const expectedArgs = [
|
||||
`${path.startsWith(".claude") ? "${CLAUDE_PLUGIN_ROOT}/" : "./"}scripts/plugin-mcp-launcher.mjs`,
|
||||
packedManifest.version,
|
||||
];
|
||||
if (
|
||||
manifest.version !== packedManifest.version ||
|
||||
manifest.skills !== "./skills" ||
|
||||
(path.startsWith(".codex")
|
||||
? manifest.mcpServers !== "./.codex-plugin/mcp.json" ||
|
||||
serverConfig.mcpServers?.rea?.cwd !== "." ||
|
||||
serverConfig.mcpServers?.rea?.startup_timeout_sec !== 30
|
||||
: false) ||
|
||||
serverConfig.mcpServers?.rea?.command !== "node" ||
|
||||
JSON.stringify(serverConfig.mcpServers?.rea?.args) !==
|
||||
JSON.stringify(expectedArgs)
|
||||
)
|
||||
) {
|
||||
throw new Error("package contained generated Python bytecode");
|
||||
throw new Error(
|
||||
`Packaged plugin ${path} does not match the exact runtime and canonical skill release`,
|
||||
);
|
||||
const icons = path.startsWith(".codex")
|
||||
? [manifest.interface?.logo, manifest.interface?.composerIcon]
|
||||
: [manifest.icon];
|
||||
for (const icon of icons)
|
||||
if (
|
||||
icon !== "./assets/rea-plugin.png" ||
|
||||
!packedFiles.includes(`package/${icon.slice(2)}`)
|
||||
)
|
||||
throw new Error(`Packaged plugin ${path} is missing its shared icon`);
|
||||
}
|
||||
if (packedFiles.includes("package/.mcp.json"))
|
||||
throw new Error("Plugin metadata must not register a project MCP server");
|
||||
if (!packedFiles.includes("package/.codex-plugin/mcp.json"))
|
||||
throw new Error("Packaged Codex MCP server declaration is missing");
|
||||
if (!packedFiles.includes("package/scripts/plugin-mcp-launcher.mjs"))
|
||||
throw new Error("Packaged plugin launcher is missing");
|
||||
if (
|
||||
skill.includes(`${packedManifest.name}@latest`) ||
|
||||
!skill.includes(`${packedManifest.name}@${packedManifest.version}`)
|
||||
)
|
||||
throw new Error(
|
||||
"Packaged plugin skill recovery is not pinned to the shipped runtime",
|
||||
);
|
||||
await verifyPackedBridge({ tarball, packedFiles, commands });
|
||||
return { tarball };
|
||||
}
|
||||
Loaded 100 of 497 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user