perf(app): reduce startup websocket work

Make provider catalogs compact and content-addressed so repeat launches can reuse the persisted snapshot instead of transferring it again. Remove eager global catalog loading and the stopped-agent defensive tail fetch, while keeping sequence-gap and reconnect recovery as the correctness paths. Add an opt-in production-ID Android profiling build for local startup traces.
This commit is contained in:
Mohamed Boudra
2026-08-04 15:45:55 +02:00
parent 8e5b6876b4
commit 7dbeff8290
36 changed files with 1209 additions and 171 deletions
+150 -1
View File
@@ -1,6 +1,11 @@
import { afterEach, expect, expectTypeOf, test, vi } from "vitest";
import { z } from "zod";
import { DaemonClient, type DaemonTransport, type Logger } from "./daemon-client";
import {
DaemonClient,
type DaemonClientTrace,
type DaemonTransport,
type Logger,
} from "./daemon-client";
import { CLIENT_CAPS } from "@getpaseo/protocol/client-capabilities";
import { BROWSER_AUTOMATION_COMMAND_NAMES } from "@getpaseo/protocol/browser-automation/rpc-schemas";
import {
@@ -34,6 +39,24 @@ function createMockLogger() {
};
}
interface TraceRecord {
phase: "begin" | "end";
name?: string;
args?: Record<string, string>;
}
function createTraceRecorder(): { trace: DaemonClientTrace; records: TraceRecord[] } {
const records: TraceRecord[] = [];
return {
trace: {
isEnabled: () => true,
beginSection: (name, args) => records.push({ phase: "begin", name, args }),
endSection: () => records.push({ phase: "end" }),
},
records,
};
}
function createMockTransport() {
const sent: Array<string | Uint8Array | ArrayBuffer> = [];
@@ -161,6 +184,56 @@ afterEach(async () => {
vi.unstubAllGlobals();
});
test("traces WebSocket frames, message types, and JSON parse duration", async () => {
const mock = createMockTransport();
const recorder = createTraceRecorder();
const client = new DaemonClient({
url: "ws://test",
clientId: "trace_unit_test",
transportFactory: () => mock.transport,
reconnect: { enabled: false },
trace: recorder.trace,
});
clients.push(client);
const connectPromise = client.connect();
mock.triggerOpen({ preserveSent: true });
await connectPromise;
expect(recorder.records).toEqual([
{
phase: "begin",
name: "paseo.ws.message.outbound",
args: { envelopeType: "hello", messageType: "hello" },
},
{ phase: "end" },
{
phase: "begin",
name: "paseo.ws.frame.outbound",
args: { kind: "text", size: expect.any(String) },
},
{ phase: "end" },
{
phase: "begin",
name: "paseo.ws.frame.inbound",
args: { kind: "text", size: expect.any(String) },
},
{
phase: "begin",
name: "paseo.ws.json.parse",
args: { size: expect.any(String) },
},
{ phase: "end" },
{
phase: "begin",
name: "paseo.ws.message.inbound",
args: { envelopeType: "session", messageType: "status" },
},
{ phase: "end" },
{ phase: "end" },
]);
});
test("does not infer browser automation capabilities from Electron runtime", async () => {
vi.stubGlobal("navigator", {
userAgent:
@@ -664,6 +737,7 @@ test("advertises client capabilities in hello", async () => {
clientType: "cli",
protocolVersion: 1,
capabilities: {
compact_provider_snapshots: true,
custom_mode_icons: true,
project_updates: true,
provider_subagents: true,
@@ -4384,6 +4458,81 @@ test("lists available providers via RPC", async () => {
});
});
test("requests provider snapshots conditionally and expands the compact response", async () => {
const mock = createMockTransport();
const client = new DaemonClient({
url: "ws://test",
clientId: "clsk_provider_snapshot_test",
reconnect: { enabled: false },
transportFactory: () => mock.transport,
});
clients.push(client);
const connectPromise = client.connect();
mock.triggerOpen();
await connectPromise;
const promise = client.getProvidersSnapshot({ cwd: "/repo", ifNoneMatch: "previous-hash" });
const request = parseSentFrame(mock.sent[0]);
expect(request).toMatchObject({
type: "get_providers_snapshot_request",
cwd: "/repo",
ifNoneMatch: "previous-hash",
});
mock.triggerMessage(
wrapSessionMessage({
type: "get_providers_snapshot_response",
payload: {
entries: [],
compactSnapshot: {
entries: [
{
provider: "pi",
status: "ready",
enabled: true,
models: [
{ id: "model-a", label: "Model A", thinkingSet: 0 },
{ id: "model-b", label: "Model B", thinkingSet: 0 },
],
},
],
thinkingSets: [
{
options: [{ id: "high", label: "High", isDefault: true }],
defaultOptionId: "high",
},
],
},
snapshotHash: "next-hash",
generatedAt: "2026-08-04T00:00:00.000Z",
requestId: request.requestId,
},
}),
);
const response = await promise;
expect(response.entries[0]?.models).toEqual([
{
provider: "pi",
id: "model-a",
label: "Model A",
thinkingOptions: [{ id: "high", label: "High", isDefault: true }],
defaultThinkingOptionId: "high",
},
{
provider: "pi",
id: "model-b",
label: "Model B",
thinkingOptions: [{ id: "high", label: "High", isDefault: true }],
defaultThinkingOptionId: "high",
},
]);
expect(response.entries[0]?.models?.[0]?.thinkingOptions).toBe(
response.entries[0]?.models?.[1]?.thinkingOptions,
);
});
test("lists commands with draft config via RPC", async () => {
const logger = createMockLogger();
const mock = createMockTransport();