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:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user