rea-agents 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +34 -12
  2. package/dist/application/AnalysisSnapshotCache.js +8 -6
  3. package/dist/application/ArtifactGraphConstruction.js +11 -2
  4. package/dist/application/ArtifactInventory.js +3 -2
  5. package/dist/application/AuthorizedArtifactInventory.js +2 -1
  6. package/dist/application/BinarySession.js +12 -7
  7. package/dist/application/BoundedJsonFiles.js +6 -29
  8. package/dist/application/BrowserEvidence.js +35 -0
  9. package/dist/application/BrowserObservationPort.js +1 -0
  10. package/dist/application/BrowserObservationService.js +52 -0
  11. package/dist/application/CapabilityInventory.js +7 -0
  12. package/dist/application/ConfiguredRoots.js +19 -0
  13. package/dist/application/CrossVersionInvestigation.js +6 -0
  14. package/dist/application/DeferredFileAuthorization.js +61 -0
  15. package/dist/application/DirectAnalysis.js +38 -18
  16. package/dist/application/EvidenceLedger.js +6 -5
  17. package/dist/application/InvestigationWorkspaceStore.js +2 -26
  18. package/dist/application/ProcessCaptureAuthority.js +3 -2
  19. package/dist/application/ProcessCaptureLifecycle.js +18 -8
  20. package/dist/application/ProcessCheckpoints.js +1 -1
  21. package/dist/application/ProcessCli.js +17 -5
  22. package/dist/application/ProcessHarness.js +6 -19
  23. package/dist/application/ProcessNormalization.js +17 -2
  24. package/dist/application/ProcessOwnership.js +4 -3
  25. package/dist/application/ProcessSampling.js +52 -17
  26. package/dist/application/ProjectPermissionStore.js +106 -3
  27. package/dist/application/ReferenceSourceImportPolicy.js +3 -2
  28. package/dist/application/TerminalRenderer.js +1 -1
  29. package/dist/artifacts/MachOSliceArtifactReader.js +1 -1
  30. package/dist/artifacts/NativeDmgArtifactReader.js +1 -1
  31. package/dist/artifacts/SafeOutputTree.js +3 -3
  32. package/dist/browser/CdpBrowserProvider.js +158 -0
  33. package/dist/browser/CdpCaptureDocuments.js +140 -0
  34. package/dist/browser/CdpCaptureEvents.js +248 -0
  35. package/dist/browser/CdpCaptureStorage.js +53 -0
  36. package/dist/browser/CdpCaptureValues.js +70 -0
  37. package/dist/browser/CdpConnection.js +218 -0
  38. package/dist/browser/CdpEndpoint.js +114 -0
  39. package/dist/browser/CdpOptionalCommand.js +15 -0
  40. package/dist/browser/CdpPageCapture.js +273 -0
  41. package/dist/catalogIdentity.js +2 -0
  42. package/dist/cli.js +2 -0
  43. package/dist/cliBrowserCommands.js +136 -0
  44. package/dist/cliInvestigationCommands.js +19 -34
  45. package/dist/cliPolicyCommands.js +13 -10
  46. package/dist/config.js +44 -0
  47. package/dist/contracts/browserToolContracts.js +80 -0
  48. package/dist/contracts/errorSchemas.js +2 -0
  49. package/dist/contracts/toolContracts.js +3 -5
  50. package/dist/domain/browserObservation.js +361 -0
  51. package/dist/domain/changedBehavior.js +4 -15
  52. package/dist/domain/errors.js +41 -0
  53. package/dist/domain/jsonLimits.js +26 -0
  54. package/dist/domain/permissionPolicy.js +6 -2
  55. package/dist/domain/processCapture.js +3 -1
  56. package/dist/domain/processComparison.js +22 -34
  57. package/dist/domain/reconstructionVerification.js +2 -15
  58. package/dist/domain/reconstructionVerificationSchemas.js +2 -0
  59. package/dist/domain/staticRuntimeCorrelation.js +2 -0
  60. package/dist/domain/symbolAnalysis.js +1 -1
  61. package/dist/generatedPackageMetadata.js +2 -2
  62. package/dist/main.js +6 -0
  63. package/dist/native/CommandRunner.js +11 -4
  64. package/dist/server/createServer.js +7 -0
  65. package/dist/server/registerBrowserTools.js +36 -0
  66. package/dist/server/registerInvestigationTools.js +30 -35
  67. package/dist/server/registerProcessComparisonTool.js +2 -0
  68. package/dist/server/registerSessionTools.js +15 -2
  69. package/dist/server/runDerivedOperation.js +10 -2
  70. package/package.json +2 -1
  71. package/scripts/rea.mjs +1 -1
  72. package/skills/rea-analysis/SKILL.md +18 -3
@@ -7,7 +7,7 @@ import { parseEvidence } from "./evidence.js";
7
7
  import { parseEvidenceBundle } from "./evidenceBundle.js";
8
8
  import { functionComparisonResultSchema } from "./functionComparison.js";
9
9
  import { functionDossierSchema } from "./hopperValues.js";
10
- import { processCaptureComparisonSchema, processCaptureSchema, } from "./processCapture.js";
10
+ import { deriveProcessComparisonStatus, PROCESS_COMPARISON_DIMENSIONS, processCaptureComparisonSchema, processCaptureSchema, } from "./processCapture.js";
11
11
  import { reconstructionClaimResultSchema, reconstructionSpecificationSchema, reconstructionVerificationResultSchema, } from "./reconstructionVerificationSchemas.js";
12
12
  import { reconstructionClaimUnknowns, reconstructionProbes, reconstructionUnknownHeads, } from "./reconstructionUnknowns.js";
13
13
  const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
@@ -219,20 +219,7 @@ const observedStatus = (claim, evidence) => {
219
219
  return result.status;
220
220
  };
221
221
  const validateProcessStatus = (result) => {
222
- const dimensions = [
223
- result.terminal,
224
- result.exit,
225
- result.filesystem,
226
- result.protocol,
227
- result.process,
228
- ];
229
- const expected = dimensions.includes("truncated")
230
- ? "truncated"
231
- : dimensions.some((status) => ["added", "removed", "changed"].includes(status))
232
- ? "changed"
233
- : dimensions.includes("unknown")
234
- ? "unknown"
235
- : "unchanged";
222
+ const expected = deriveProcessComparisonStatus(PROCESS_COMPARISON_DIMENSIONS.map((dimension) => result[dimension]));
236
223
  if (result.status !== expected)
237
224
  throw new TypeError("Process comparison status contradicts its dimensions");
238
225
  };
@@ -17,10 +17,12 @@ const behavioralClaimSchema = z.object({
17
17
  dimension: z.enum([
18
18
  "overall",
19
19
  "terminal",
20
+ "interaction",
20
21
  "exit",
21
22
  "filesystem",
22
23
  "protocol",
23
24
  "process",
25
+ "shim",
24
26
  ]),
25
27
  });
26
28
  const functionClaimSchema = z.object({
@@ -18,10 +18,12 @@ const functionDimensionSchema = z.enum([
18
18
  ]);
19
19
  const runtimeDimensionSchema = z.enum([
20
20
  "terminal",
21
+ "interaction",
21
22
  "exit",
22
23
  "filesystem",
23
24
  "protocol",
24
25
  "process",
26
+ "shim",
25
27
  ]);
26
28
  const expectedPatternSchema = z.enum([
27
29
  "cochanged",
@@ -14,7 +14,7 @@ export const discoverSwiftClasses = (procedures, pattern) => {
14
14
  /** Select and deduplicate Objective-C class labels. */
15
15
  export const discoverObjcClasses = (names, pattern) => {
16
16
  const classes = uniqueByName(names
17
- .filter(({ name }) => ["OBJC_CLASS", "OBJC_$_CLASS"].some((marker) => name.includes(marker)) || name.startsWith("_OBJC_"))
17
+ .filter(({ name }) => ["OBJC_CLASS", "OBJC_$_CLASS"].some((marker) => name.includes(marker)))
18
18
  .filter(({ name }) => pattern.length === 0 || name.includes(pattern)));
19
19
  return {
20
20
  count: classes.length,
@@ -1,9 +1,9 @@
1
1
  /** Generated from package.json and package-lock.json; do not edit. */
2
2
  export const PACKAGE_METADATA = {
3
3
  name: "rea-agents",
4
- version: "1.4.0",
4
+ version: "1.5.0",
5
5
  serverSdkVersion: "2.0.0-beta.4",
6
6
  clientSdkVersion: "2.0.0-beta.4",
7
7
  coreSdkVersion: "2.0.0-beta.4",
8
- skillVersion: "12",
8
+ skillVersion: "13",
9
9
  };
package/dist/main.js CHANGED
@@ -9,6 +9,7 @@ import { createLogger } from "./logger.js";
9
9
  import { projectAnalysisError } from "./domain/errors.js";
10
10
  import { readProjectPermissionStore } from "./application/ProjectPermissionStore.js";
11
11
  import { loadConfiguredPermissionAuthority } from "./application/PermissionConfiguration.js";
12
+ import { CdpBrowserProvider } from "./browser/CdpBrowserProvider.js";
12
13
  const MCP_CONNECTION_LOST = "REA lost its MCP client connection. Restart REA from your MCP client.";
13
14
  const MCP_CONNECTION_START_FAILED = "REA could not start its MCP connection. Restart REA from your MCP client; run `rea doctor` if it fails again.";
14
15
  const MCP_SHUTDOWN_FAILED = "REA could not close cleanly. End the REA process before starting it again.";
@@ -50,6 +51,7 @@ export const run = async (dependencies = runtimeDependencies()) => {
50
51
  return 1;
51
52
  }
52
53
  const session = createBinarySession(config.value, logger);
54
+ const browserObservation = new CdpBrowserProvider();
53
55
  if (config.value.hopperTargetPath !== undefined) {
54
56
  const opened = await session.open(config.value.hopperTargetPath, {
55
57
  targetKind: config.value.hopperTargetKind,
@@ -90,10 +92,14 @@ export const run = async (dependencies = runtimeDependencies()) => {
90
92
  investigationInputRoots: runtimeInvestigationRoots,
91
93
  analysisSnapshotFilePolicy: runtimeSnapshotPolicy,
92
94
  permissionAuthority: permissionAuthority.value,
95
+ browserObservation,
93
96
  artifactIntegrityContinueEnabled: () => currentConfig.artifactIntegrityContinueEnabled,
94
97
  availabilityPolicy: () => ({
95
98
  processCaptureEnabled: currentConfig.processExecutionPolicy.enabled,
96
99
  evidenceFileRoots: currentConfig.evidenceFilePolicy.roots.length,
100
+ browserObservationEnabled: currentConfig.browserObservationEnabled &&
101
+ currentConfig.browserCdpEndpoints.length > 0 &&
102
+ currentConfig.browserAllowedOrigins.length > 0,
97
103
  }),
98
104
  });
99
105
  liveServers.add(server);
@@ -16,7 +16,11 @@ export class NativeCommandFailure extends Error {
16
16
  }
17
17
  /** Run allowlisted Xcode tools directly with bounded output and no shell. */
18
18
  export class XcrunCommandRunner {
19
+ resolveTool;
19
20
  #resolved = new Map();
21
+ constructor(resolveTool = (tool) => resolveXcrunTool(tool)) {
22
+ this.resolveTool = resolveTool;
23
+ }
20
24
  run(tool, arguments_, options) {
21
25
  if (!ALLOWED_TOOLS.has(tool))
22
26
  return Promise.resolve(err(new NativeCommandFailure(tool, "unavailable")));
@@ -37,13 +41,16 @@ export class XcrunCommandRunner {
37
41
  : captured;
38
42
  });
39
43
  }
40
- #resolve(tool) {
44
+ async #resolve(tool) {
41
45
  const existing = this.#resolved.get(tool);
42
46
  if (existing !== undefined)
43
47
  return existing;
44
- const pending = resolveTool(tool);
48
+ const pending = this.resolveTool(tool);
45
49
  this.#resolved.set(tool, pending);
46
- return pending;
50
+ const resolved = await pending;
51
+ if (!resolved.ok && this.#resolved.get(tool) === pending)
52
+ this.#resolved.delete(tool);
53
+ return resolved;
47
54
  }
48
55
  }
49
56
  const ALLOWED_TOOLS = new Set([
@@ -59,7 +66,7 @@ const ALLOWED_TOOLS = new Set([
59
66
  "swift-demangle",
60
67
  "vtool",
61
68
  ]);
62
- const resolveTool = async (tool) => {
69
+ const resolveXcrunTool = async (tool) => {
63
70
  const found = await captureProcess("/usr/bin/xcrun", ["--find", tool], "xcrun", { timeoutMs: 5_000, maxOutputBytes: 64 * 1024 });
64
71
  if (!found.ok || found.value.exitCode !== 0)
65
72
  return err(new NativeCommandFailure(tool, "unavailable"));
@@ -9,6 +9,7 @@ import { silentLogger } from "../logger.js";
9
9
  import { registerGuidedPrompts } from "./registerPrompts.js";
10
10
  import { registerEvidenceResources } from "./registerEvidenceResources.js";
11
11
  import { createServerIdentity } from "../serverIdentity.js";
12
+ import { registerBrowserTools } from "./registerBrowserTools.js";
12
13
  /**
13
14
  * Construct one MCP server without acquiring subprocess resources.
14
15
  * Supplying a session adds target lifecycle tools; omitting it retains the
@@ -93,6 +94,12 @@ export const createServer = (analysis, session, options = {}) => {
93
94
  ? {}
94
95
  : { permissionAuthority: options.permissionAuthority }),
95
96
  });
97
+ registerBrowserTools(server, {
98
+ logger: toolLogger,
99
+ browser: options.browserObservation,
100
+ permissionAuthority: options.permissionAuthority,
101
+ recordEvidence,
102
+ });
96
103
  registerGuidedPrompts(server, analysis, session);
97
104
  if (session !== undefined) {
98
105
  registerEvidenceResources(server, session);
@@ -0,0 +1,36 @@
1
+ import { inspectWebPage, listBrowserTargets, } from "../application/BrowserObservationService.js";
2
+ import { BROWSER_TOOL_CONTRACTS } from "../contracts/browserToolContracts.js";
3
+ import { inspectWebPageInputSchema, listBrowserTargetsInputSchema, } from "../domain/browserObservation.js";
4
+ import { logToolExecution } from "./toolLogging.js";
5
+ import { toCallToolResult } from "./toolResult.js";
6
+ import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
7
+ import { mcpProgressReporter } from "./mcpProgress.js";
8
+ /** Register browser tools even when policy/provider availability denies execution. */
9
+ export const registerBrowserTools = (server, options) => {
10
+ const [listContract, inspectContract] = BROWSER_TOOL_CONTRACTS;
11
+ server.registerTool(listContract.name, toolRegistrationOptions(listContract), async (input, context) => {
12
+ const parsed = listBrowserTargetsInputSchema.parse(input);
13
+ const result = await logToolExecution(options.logger, listContract.name, () => listBrowserTargets(options.browser, options.permissionAuthority, parsed, { signal: context.mcpReq.signal }));
14
+ if (!result.ok)
15
+ return toCallToolResult(result, listContract);
16
+ return evidenceResult(options, listContract, result.value);
17
+ });
18
+ server.registerTool(inspectContract.name, toolRegistrationOptions(inspectContract), async (input, context) => {
19
+ const parsed = inspectWebPageInputSchema.parse(input);
20
+ const result = await logToolExecution(options.logger, inspectContract.name, () => inspectWebPage(options.browser, options.permissionAuthority, parsed, {
21
+ signal: context.mcpReq.signal,
22
+ progress: mcpProgressReporter(context),
23
+ }));
24
+ if (!result.ok)
25
+ return toCallToolResult(result, inspectContract);
26
+ return evidenceResult(options, inspectContract, result.value);
27
+ });
28
+ };
29
+ const evidenceResult = (options, contract, evidence) => {
30
+ const recorded = options.recordEvidence?.(evidence);
31
+ return recorded !== undefined && !recorded.ok
32
+ ? toCallToolResult(recorded, contract)
33
+ : toCallToolResult({ ok: true, value: evidence }, contract, {
34
+ evidenceResourcesAvailable: recorded !== undefined,
35
+ });
36
+ };
@@ -10,9 +10,9 @@ import { err, ok } from "../domain/result.js";
10
10
  import { CALL_PATH_PROVIDER, CHANGED_BEHAVIOR_PROVIDER, RECONSTRUCTION_PROVIDER, STATIC_RUNTIME_PROVIDER, } from "./sessionToolPolicies.js";
11
11
  import { toCallToolResult } from "./toolResult.js";
12
12
  import { recordDerivedEvidence } from "./recordDerivedEvidence.js";
13
- import { AnalysisProtocolError, PermissionRequiredError, } from "../domain/errors.js";
14
13
  import { mcpProgressReporter } from "./mcpProgress.js";
15
14
  import { runDerivedOperation } from "./runDerivedOperation.js";
15
+ import { authorizeFileReadWithDeferredWrite, authorizeRootPermission, } from "../application/DeferredFileAuthorization.js";
16
16
  /** Register Evidence-composed differential investigation workflows. */
17
17
  export const registerInvestigationTools = (server, session, contracts, policies) => {
18
18
  registerChangedBehavior(server, session, contracts[0], policies);
@@ -33,42 +33,37 @@ const registerChangedBehavior = (server, session, contract, policies) => {
33
33
  const parsed = changedBehaviorInputSchema.parse(input);
34
34
  if (parsed.investigation_run !== undefined) {
35
35
  const progress = mcpProgressReporter(context);
36
+ let workspaceAuthorization;
36
37
  if (policies.permissionAuthority !== undefined) {
37
- const scopes = [
38
- {
39
- capability: "investigation_workspace_read",
40
- roots: [parsed.investigation_run.workspace_path],
41
- },
42
- {
43
- capability: "investigation_workspace_write",
44
- roots: [parsed.investigation_run.workspace_path],
45
- },
46
- {
47
- capability: "investigation_input",
48
- roots: [
49
- parsed.investigation_run.left_path,
50
- parsed.investigation_run.right_path,
51
- ],
52
- },
53
- ];
54
- for (const scope of scopes) {
55
- const authorized = await policies.permissionAuthority.authorize({
56
- ...scope,
57
- executables: [],
58
- environment_names: [],
59
- network: "none",
60
- mount: false,
61
- operation_identity: `${contract.name}:${scope.capability}:${scope.roots.join(":")}`,
62
- }, scope.capability === "investigation_input" ? "read" : "write");
63
- if (!authorized.ok)
64
- return toCallToolResult(err(authorized.error instanceof PermissionRequiredError
65
- ? authorized.error
66
- : new AnalysisProtocolError(authorized.error.message, {
67
- cause: authorized.error,
68
- })), contract);
69
- }
38
+ const authorizedWorkspace = await authorizeFileReadWithDeferredWrite(policies.permissionAuthority, {
39
+ path: parsed.investigation_run.workspace_path,
40
+ readCapability: "investigation_workspace_read",
41
+ writeCapability: "investigation_workspace_write",
42
+ operation: contract.name,
43
+ });
44
+ if (!authorizedWorkspace.ok)
45
+ return toCallToolResult(authorizedWorkspace, contract);
46
+ workspaceAuthorization = authorizedWorkspace.value;
47
+ const authorizedInput = await authorizeRootPermission(policies.permissionAuthority, {
48
+ capability: "investigation_input",
49
+ roots: [
50
+ parsed.investigation_run.left_path,
51
+ parsed.investigation_run.right_path,
52
+ ],
53
+ access: "read",
54
+ operation: contract.name,
55
+ });
56
+ if (!authorizedInput.ok)
57
+ return toCallToolResult(authorizedInput, contract);
70
58
  }
71
- const investigated = await runCrossVersionInvestigation(parsed.investigation_run, policies.evidenceFiles, investigationExecution(session, context.mcpReq.signal, policies.inputRoots, policies.integrityContinueEnabled?.() ?? false, progress));
59
+ const investigated = await runCrossVersionInvestigation(parsed.investigation_run, policies.evidenceFiles, {
60
+ ...investigationExecution(session, context.mcpReq.signal, policies.inputRoots, policies.integrityContinueEnabled?.() ?? false, progress),
61
+ ...(workspaceAuthorization === undefined
62
+ ? {}
63
+ : {
64
+ authorizeWorkspaceWrite: workspaceAuthorization.authorizeWrite,
65
+ }),
66
+ });
72
67
  if (!investigated.ok)
73
68
  return toCallToolResult(investigated, contract);
74
69
  const workspace = investigated.value.workspace;
@@ -95,10 +95,12 @@ const comparisonUnknownInput = (parsed, comparison) => {
95
95
  return undefined;
96
96
  const differingScopes = [
97
97
  ["terminal", comparison.terminal],
98
+ ["interaction", comparison.interaction],
98
99
  ["exit", comparison.exit],
99
100
  ["filesystem", comparison.filesystem],
100
101
  ["protocol", comparison.protocol],
101
102
  ["process", comparison.process],
103
+ ["shim", comparison.shim],
102
104
  ]
103
105
  .filter(([, status]) => status !== "unchanged")
104
106
  .map(([scope]) => scope)
@@ -170,6 +170,8 @@ const registerEvidenceTools = ({ server, session, exportContract, importContract
170
170
  if (!loaded.ok)
171
171
  return toCallToolResult(loaded, importContract);
172
172
  const imported = session.importEvidenceBundle(loaded.value);
173
+ if (imported.ok && imported.value > 0)
174
+ server.sendResourceListChanged();
173
175
  return imported.ok
174
176
  ? toCallToolResult(ok({
175
177
  imported: imported.value,
@@ -213,13 +215,23 @@ const registerUnknownTools = ({ server, session, contracts: [listContract, recor
213
215
  inputSchema: recordContract.inputSchema,
214
216
  outputSchema: recordContract.outputSchema,
215
217
  annotations: recordContract.annotations,
216
- }, (input) => toCallToolResult(session.recordUnknown(recordUnknownInputSchema.parse(input)), recordContract));
218
+ }, (input) => {
219
+ const recorded = session.recordUnknown(recordUnknownInputSchema.parse(input));
220
+ if (recorded.ok)
221
+ server.sendResourceListChanged();
222
+ return toCallToolResult(recorded, recordContract);
223
+ });
217
224
  server.registerTool(updateContract.name, {
218
225
  description: updateContract.description,
219
226
  inputSchema: updateContract.inputSchema,
220
227
  outputSchema: updateContract.outputSchema,
221
228
  annotations: updateContract.annotations,
222
- }, (input) => toCallToolResult(session.updateUnknown(updateUnknownInputSchema.parse(input)), updateContract));
229
+ }, (input) => {
230
+ const updated = session.updateUnknown(updateUnknownInputSchema.parse(input));
231
+ if (updated.ok)
232
+ server.sendResourceListChanged();
233
+ return toCallToolResult(updated, updateContract);
234
+ });
223
235
  server.registerTool(verifyContract.name, {
224
236
  description: verifyContract.description,
225
237
  inputSchema: verifyContract.inputSchema,
@@ -342,6 +354,7 @@ export const registerSessionTools = (server, session, logger, options = {}) => {
342
354
  (() => ({
343
355
  processCaptureEnabled: processPolicy.enabled,
344
356
  evidenceFileRoots: evidenceFilePolicy.roots.length,
357
+ browserObservationEnabled: false,
345
358
  })),
346
359
  ...(options.permissionAuthority === undefined
347
360
  ? {}
@@ -1,5 +1,5 @@
1
1
  import { setImmediate } from "node:timers/promises";
2
- import { AnalysisCancelledError } from "../domain/errors.js";
2
+ import { AnalysisCancelledError, AnalysisInputError, } from "../domain/errors.js";
3
3
  import { err, ok } from "../domain/result.js";
4
4
  import { mcpProgressReporter } from "./mcpProgress.js";
5
5
  /** Run synchronous derived work with transport yields and a publication guard. */
@@ -14,7 +14,15 @@ export const runDerivedOperation = async (context, operation, compute) => {
14
14
  await setImmediate();
15
15
  if (context.mcpReq.signal.aborted)
16
16
  return err(new AnalysisCancelledError(operation));
17
- const value = compute();
17
+ let value;
18
+ try {
19
+ value = compute();
20
+ }
21
+ catch (cause) {
22
+ return err(cause instanceof AnalysisCancelledError
23
+ ? cause
24
+ : new AnalysisInputError(operation, { cause }));
25
+ }
18
26
  await progress.report({
19
27
  phase: "compute",
20
28
  completed: 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rea-agents",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Reverse engineer anything from your terminal or agent with one CLI and MCP server.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -70,6 +70,7 @@
70
70
  "test:watch": "vitest",
71
71
  "typecheck": "tsc --noEmit",
72
72
  "verify:hopper": "npm run build && node scripts/verify-real-hopper.mjs",
73
+ "verify:browser": "npm run build && node scripts/verify-real-browser.mjs",
73
74
  "verify:hopper:linux": "npm run build && node scripts/verify-real-hopper-linux.mjs",
74
75
  "verify:setup:linux": "npm run build && node scripts/verify-linux-setup-e2e.mjs",
75
76
  "build:fixtures": "node scripts/build-conformance-fixtures.mjs",
package/scripts/rea.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // Route production MCP before importing Incur. Incur owns registration helpers
4
- // such as `mcp add`, while only dist/main.js may serve the 68-tool stdio server.
4
+ // such as `mcp add`, while only dist/main.js may serve the 70-tool stdio server.
5
5
  const args = process.argv.slice(2);
6
6
  const { default: packageJson } = await import("../package.json", {
7
7
  with: { type: "json" },
@@ -1,15 +1,30 @@
1
1
  ---
2
2
  name: rea-analysis
3
- description: Reverse engineer apps with REA. Explore how features work, then build a version tailored to your project.
3
+ description: Reverse engineer apps and websites with REA. Explore how features work, then build a version tailored to your project.
4
4
  metadata:
5
- version: "12"
6
- tool_count: 68
5
+ version: "13"
6
+ tool_count: 70
7
7
  ---
8
8
 
9
9
  # REA
10
10
 
11
11
  Use REA when the user wants to understand how an app or feature works, compare app versions, decompile code, or build a similar feature.
12
12
 
13
+ For a website already open in a user-owned Chrome-family browser, use
14
+ `list_browser_targets` and `inspect_web_page` instead of opening a binary. Ask
15
+ the user to approve the exact page origins and loopback CDP endpoint before the
16
+ first call. Browser observation is disabled unless the operator configured
17
+ `REA_BROWSER_OBSERVE_ENABLED`, `REA_BROWSER_CDP_ENDPOINTS_JSON`, and
18
+ `REA_BROWSER_ALLOWED_ORIGINS_JSON` before starting REA.
19
+
20
+ Browser inspection is passive. Never claim that it clicked, navigated,
21
+ evaluated page JavaScript, captured prior activity, or contained the page's
22
+ network. Query values, headers, bodies, cookies, storage values, console values,
23
+ and WebSocket payloads are deliberately absent. Request script sources or
24
+ storage key names only when the user separately approves those more sensitive
25
+ outputs. Treat truncation and attach-window coverage as explicit limitations,
26
+ and cite the returned Evidence v2 IDs.
27
+
13
28
  ## Understand the request
14
29
 
15
30
  Identify the app and what the user wants to understand or build. Do not ask for information they already provided.