perf: reduce MCP launcher and browser capture overhead (#4350)

* perf(browser): 为单次捕获复用脚本资源索引

* docs(setup): 说明多会话直接启动以减少 npm 驻留
This commit is contained in:
郭立 Guo Li
2026-10-17 15:51:23 +08:00
committed by GitHub
parent 509d4c8f56
commit 0cd4eafd61
4 changed files with 157 additions and 49 deletions
+15
View File
@@ -1236,6 +1236,21 @@ on the shell `PATH`. Use the setup command above for the guided setup
journey, `npx -y rea-agents@latest` for unattended one-off commands, or install globally
with `npm install --global rea-agents` for a shell-visible `rea` command.
For many concurrent agent sessions, run setup directly from a global installation
to avoid keeping a separate npm launcher process for each MCP connection:
```bash
npm install --global rea-agents
rea setup
```
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.
The optional curl wrapper installs only the global npm package:
```bash
+20 -16
View File
@@ -1,6 +1,6 @@
import type { WebPageInspection } from "../domain/browserObservationSchemas.js";
import {
reconcileCapturedWebScript,
createCapturedWebScriptReconciler,
stableWebScriptKey,
} from "../domain/webInventory.js";
import type { CapturedResource } from "./CdpCaptureDocuments.js";
@@ -32,8 +32,9 @@ interface ScriptDraftState {
readonly context: CaptureContext;
readonly events: CdpCaptureEvents;
readonly frameIds: ReadonlySet<string>;
readonly rawResources: readonly CapturedResource[];
readonly resources: WebPageInspection["resources"];
readonly reconcileResource: ReturnType<
typeof createCapturedWebScriptReconciler
>;
}
type ScriptDraftItem = Omit<
@@ -45,19 +46,23 @@ export const captureScripts = async (
options: CaptureScriptsOptions,
): Promise<CapturedScripts> => {
const { context, events, rawResources, resources, frameIds } = options;
const state: ScriptDraftState = {
context,
events,
frameIds,
rawResources,
resources,
};
let state: ScriptDraftState | undefined;
const drafts: {
readonly item: ScriptDraftItem;
readonly sourceMapRawUrl: string | null;
}[] = [];
for (const script of events.scripts.values())
for (const script of events.scripts.values()) {
state ??= {
context,
events,
frameIds,
reconcileResource: createCapturedWebScriptReconciler(
rawResources,
resources,
),
};
drafts.push(await buildScriptDraft(script, state));
}
if (!context.input.include_script_sources)
events.completeness.exclude(
"script_sources",
@@ -91,11 +96,10 @@ const buildScriptDraft = async (
item: {
...inventoryScript,
origin: script.origin,
resource_reconciliation: reconcileCapturedWebScript(
{ ...inventoryScript, rawUrl: script.rawUrl },
state.rawResources,
state.resources,
),
resource_reconciliation: state.reconcileResource({
...inventoryScript,
rawUrl: script.rawUrl,
}),
source,
},
sourceMapRawUrl: script.sourceMapRawUrl,
+88 -13
View File
@@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import {
reconcileCapturedWebScript,
createCapturedWebScriptReconciler,
reconcileWebScript,
stableWebResources,
stableWebScriptKey,
@@ -11,6 +11,15 @@ import {
webTextArtifactSchema,
} from "./webContentArtifact.js";
const script = {
url: "https://app.example.test/assets/app.js",
cdp_hash: "stable-cdp-hash",
length: 1_024,
is_module: true,
language: "JavaScript",
source_map_url: "https://app.example.test/assets/app.js.map",
};
describe("web content artifacts", () => {
it("rejects altered content that reuses an artifact digest", () => {
const artifact = createWebTextArtifact("original", "text/javascript");
@@ -29,15 +38,6 @@ describe("web content artifacts", () => {
});
describe("stable web inventory", () => {
const script = {
url: "https://app.example.test/assets/app.js",
cdp_hash: "stable-cdp-hash",
length: 1_024,
is_module: true,
language: "JavaScript",
source_map_url: "https://app.example.test/assets/app.js.map",
};
it("excludes transient CDP and target identity from script keys", () => {
const first = {
...script,
@@ -134,11 +134,13 @@ describe("stable web inventory", () => {
rawResources.map(({ rawUrl: _rawUrl, ...resource }) => resource),
);
expect(
reconcileCapturedWebScript(
{ ...script, rawUrl: app.rawUrl },
createCapturedWebScriptReconciler(
rawResources,
resources,
),
)({
...script,
rawUrl: app.rawUrl,
}),
).toEqual({
status: "exact",
resource_key: resources.find(({ url }) => url === script.url)
@@ -147,3 +149,76 @@ describe("stable web inventory", () => {
}
});
});
describe("captured web resource reconciliation", () => {
it("keeps repeated raw observations ambiguous after public deduplication", () => {
const resource = {
url: script.url,
rawUrl: `${script.url}?variant=one`,
origin: "https://app.example.test",
type: "Script",
mime_type: "text/javascript",
content_size: 1_024,
};
const resources = stableWebResources(
[resource].map(({ rawUrl: _, ...r }) => r),
);
const reconcile = createCapturedWebScriptReconciler(
[resource, resource, resource],
resources,
);
expect(reconcile({ ...script, rawUrl: resource.rawUrl })).toEqual({
status: "ambiguous",
candidate_resource_keys: resources.map(
({ resource_key }) => resource_key,
),
});
});
it("preserves exact raw matches and ambiguous sanitized fallbacks in one capture", () => {
const rawResources = [1_024, 2_048].map((content_size, index) => ({
url: script.url,
rawUrl: `${script.url}?variant=${index}`,
origin: "https://app.example.test",
type: "Script",
mime_type: "text/javascript",
content_size,
}));
const resources = stableWebResources(
rawResources.map(({ rawUrl: _, ...r }) => r),
);
const reconcile = createCapturedWebScriptReconciler(
rawResources,
resources,
);
for (const resource of rawResources)
expect(reconcile({ ...script, rawUrl: resource.rawUrl })).toEqual({
status: "exact",
resource_key: resources.find(
(r) => r.content_size === resource.content_size,
)?.resource_key,
});
const fallback = { ...script, rawUrl: `${script.url}?variant=unobserved` };
const expected = {
status: "ambiguous",
candidate_resource_keys: resources.map((r) => r.resource_key).sort(),
};
expect(reconcile(fallback)).toEqual(expected);
const first = reconcile(fallback);
if (first.status !== "ambiguous") throw new Error("Expected ambiguity");
first.candidate_resource_keys.pop();
expect(reconcile(fallback)).toEqual(expected);
expect(reconcile({ ...fallback, url: `${script.url}/missing` })).toEqual({
status: "unmatched",
reason: "no_exact_sanitized_url",
});
const nextCapture = createCapturedWebScriptReconciler(
[],
resources.slice(0, 1),
);
expect(nextCapture(fallback)).toEqual({
status: "ambiguous",
candidate_resource_keys: [resources[0]?.resource_key],
});
});
});
+34 -20
View File
@@ -67,35 +67,49 @@ export const reconcileWebScript = (
return { status: "unmatched", reason: "no_exact_sanitized_url" };
};
/** Prefer exact transient raw-URL matching, then report sanitized ambiguity. */
export const reconcileCapturedWebScript = (
script: WebInventoryScriptInput & { readonly rawUrl: string },
/** Index one capture's observations without merging duplicate raw URLs. */
export const createCapturedWebScriptReconciler = (
rawResources: readonly (WebInventoryResourceInput & {
readonly rawUrl: string;
})[],
resources: readonly StableWebResource[],
): WebResourceReconciliation => {
const exact = rawResources.filter(
(resource) => resource.rawUrl === script.rawUrl,
);
if (exact.length === 1) {
const resource = exact[0];
if (resource !== undefined) {
): ((
script: WebInventoryScriptInput & {
readonly rawUrl: string;
},
) => WebResourceReconciliation) => {
// null means multiple observations, even when their public facts deduplicate.
const rawByUrl = new Map<string, (typeof rawResources)[number] | null>();
for (const resource of rawResources)
rawByUrl.set(
resource.rawUrl,
rawByUrl.has(resource.rawUrl) ? null : resource,
);
const keysByUrl = new Map<string, string[]>();
for (const resource of resources) {
const keys = keysByUrl.get(resource.url);
if (keys === undefined)
keysByUrl.set(resource.url, [resource.resource_key]);
else keys.push(resource.resource_key);
}
for (const [url, keys] of keysByUrl)
if (keys.length > 1) keysByUrl.set(url, [...new Set(keys)].sort());
return (script) => {
const resource = rawByUrl.get(script.rawUrl);
if (resource !== undefined && resource !== null) {
const { rawUrl: _rawUrl, ...publicResource } = resource;
const stable = stableWebResources([publicResource])[0];
if (stable !== undefined)
return { status: "exact", resource_key: stable.resource_key };
}
}
const candidates = resources.filter(
(resource) => resource.url === script.url,
);
if (candidates.length > 0)
return {
status: "ambiguous",
candidate_resource_keys: uniqueResourceKeys(candidates),
};
return { status: "unmatched", reason: "no_exact_sanitized_url" };
const candidates = keysByUrl.get(script.url);
if (candidates !== undefined)
return {
status: "ambiguous",
candidate_resource_keys: [...candidates],
};
return { status: "unmatched", reason: "no_exact_sanitized_url" };
};
};
/** Stable script identity excludes target IDs, CDP script IDs, and timestamps. */