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:
@@ -841,6 +841,7 @@ export class Session {
|
||||
emit: (msg) => this.emit(msg),
|
||||
isProviderVisibleToClient: (provider) => this.isProviderVisibleToClient(provider),
|
||||
supportsCustomModeIcons: () => this.supports(CLIENT_CAPS.customModeIcons),
|
||||
supportsCompactProviderSnapshots: () => this.supports(CLIENT_CAPS.compactProviderSnapshots),
|
||||
listProviderAvailability: () => this.agentManager.listProviderAvailability(),
|
||||
listDraftFeatures: (config) => this.agentManager.listDraftFeatures(config),
|
||||
},
|
||||
|
||||
@@ -13,12 +13,14 @@ import {
|
||||
} from "../../agent/provider-snapshot-manager.js";
|
||||
import type { ProviderSnapshotEntry } from "../../agent/agent-sdk-types.js";
|
||||
import type { ProviderUsageService } from "../../../services/quota-fetcher/service.js";
|
||||
import { expandProviderSnapshot } from "@getpaseo/protocol/provider-snapshot-codec";
|
||||
|
||||
type SnapshotChangeHandler = (entries: ProviderSnapshotEntry[], cwd: string) => void;
|
||||
|
||||
interface MakeOptions {
|
||||
visibleProviders?: Set<string>;
|
||||
supportsCustomModeIcons?: boolean;
|
||||
supportsCompactProviderSnapshots?: boolean;
|
||||
snapshot?: { [K in keyof ProviderSnapshotManager]?: unknown };
|
||||
usage?: { [K in keyof ProviderUsageService]?: unknown };
|
||||
host?: Partial<ProviderCatalogSessionHost>;
|
||||
@@ -49,6 +51,7 @@ function makeSubsystem(options: MakeOptions = {}) {
|
||||
emit: (msg) => emitted.push(msg),
|
||||
isProviderVisibleToClient: (provider) => visible.has(provider),
|
||||
supportsCustomModeIcons: () => options.supportsCustomModeIcons ?? false,
|
||||
supportsCompactProviderSnapshots: () => options.supportsCompactProviderSnapshots ?? false,
|
||||
listProviderAvailability: async () => [],
|
||||
listDraftFeatures: async () => [],
|
||||
...options.host,
|
||||
@@ -113,6 +116,21 @@ describe("ProviderCatalogSession", () => {
|
||||
expect(pull?.payload.entries).toEqual(push?.payload.entries);
|
||||
});
|
||||
|
||||
it("pushes the compact encoding to capable clients", () => {
|
||||
const { subsystem, emitted, pushSnapshotChange } = makeSubsystem({
|
||||
supportsCustomModeIcons: true,
|
||||
supportsCompactProviderSnapshots: true,
|
||||
});
|
||||
|
||||
subsystem.start();
|
||||
pushSnapshotChange(makeEntries());
|
||||
|
||||
const push = findByType(emitted, "providers_snapshot_update");
|
||||
expect(push?.payload.entries).toEqual([]);
|
||||
expect(push?.payload.snapshotHash).toEqual(expect.any(String));
|
||||
expect(expandProviderSnapshot(push!.payload.compactSnapshot!)).toEqual([makeEntries()[0]]);
|
||||
});
|
||||
|
||||
it("preserves custom mode icons when the client supports them", async () => {
|
||||
const { subsystem, emitted } = makeSubsystem({
|
||||
supportsCustomModeIcons: true,
|
||||
@@ -128,6 +146,60 @@ describe("ProviderCatalogSession", () => {
|
||||
expect(pull?.payload.entries[0]?.modes?.[0]?.icon).toBe("Sparkles");
|
||||
});
|
||||
|
||||
it("sends capable clients a compact snapshot and returns not-modified for its hash", async () => {
|
||||
const thinkingOptions = [
|
||||
{ id: "low", label: "Low" },
|
||||
{ id: "high", label: "High", isDefault: true },
|
||||
];
|
||||
const entries: ProviderSnapshotEntry[] = [
|
||||
{
|
||||
provider: "codex",
|
||||
status: "ready",
|
||||
enabled: true,
|
||||
models: ["one", "two"].map((id) => ({
|
||||
provider: "codex",
|
||||
id,
|
||||
label: id,
|
||||
thinkingOptions,
|
||||
defaultThinkingOptionId: "high",
|
||||
})),
|
||||
},
|
||||
];
|
||||
const { subsystem, emitted } = makeSubsystem({
|
||||
supportsCompactProviderSnapshots: true,
|
||||
snapshot: { getSnapshot: () => entries },
|
||||
});
|
||||
|
||||
await subsystem.handleGetProvidersSnapshotRequest({
|
||||
type: "get_providers_snapshot_request",
|
||||
requestId: "compact-1",
|
||||
});
|
||||
|
||||
const first = findByType(emitted, "get_providers_snapshot_response");
|
||||
expect(first?.payload.entries).toEqual([]);
|
||||
expect(first?.payload.snapshotHash).toEqual(expect.any(String));
|
||||
expect(first?.payload.compactSnapshot?.thinkingSets).toHaveLength(1);
|
||||
expect(expandProviderSnapshot(first!.payload.compactSnapshot!)).toEqual(entries);
|
||||
|
||||
await subsystem.handleGetProvidersSnapshotRequest({
|
||||
type: "get_providers_snapshot_request",
|
||||
requestId: "compact-2",
|
||||
ifNoneMatch: first?.payload.snapshotHash,
|
||||
});
|
||||
|
||||
const responses = emitted.filter(
|
||||
(message) => message.type === "get_providers_snapshot_response",
|
||||
);
|
||||
const second = responses[1];
|
||||
expect(second?.payload).toMatchObject({
|
||||
entries: [],
|
||||
snapshotHash: first?.payload.snapshotHash,
|
||||
notModified: true,
|
||||
requestId: "compact-2",
|
||||
});
|
||||
expect(second?.payload.compactSnapshot).toBeUndefined();
|
||||
});
|
||||
|
||||
it("reports a disabled provider on list_provider_models without warming the snapshot", async () => {
|
||||
// warmUpSnapshotForCwd is intentionally unstubbed: createStub throws if it is called,
|
||||
// so the disabled short-circuit is proven by the absence of a throw.
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import type pino from "pino";
|
||||
import { createHash } from "node:crypto";
|
||||
import { getErrorMessage } from "@getpaseo/protocol/error-utils";
|
||||
import {
|
||||
compactProviderSnapshot,
|
||||
type CompactProviderSnapshot,
|
||||
} from "@getpaseo/protocol/provider-snapshot-codec";
|
||||
import type { SessionInboundMessage, SessionOutboundMessage } from "../../messages.js";
|
||||
import {
|
||||
isGlobalProviderSnapshotKey,
|
||||
@@ -39,6 +44,7 @@ export interface ProviderCatalogSessionHost {
|
||||
isProviderVisibleToClient(provider: string): boolean;
|
||||
// COMPAT(customModeIcons): reads clientCapabilities live.
|
||||
supportsCustomModeIcons(): boolean;
|
||||
supportsCompactProviderSnapshots(): boolean;
|
||||
listProviderAvailability(): Promise<ProviderAvailability[]>;
|
||||
listDraftFeatures(config: AgentSessionConfig): Promise<AgentFeature[]>;
|
||||
}
|
||||
@@ -50,6 +56,19 @@ export interface ProviderCatalogSessionOptions {
|
||||
logger: pino.Logger;
|
||||
}
|
||||
|
||||
interface EncodedProviderSnapshot {
|
||||
compactSnapshot: CompactProviderSnapshot;
|
||||
snapshotHash: string;
|
||||
}
|
||||
|
||||
function encodeProviderSnapshot(entries: ProviderSnapshotEntry[]): EncodedProviderSnapshot {
|
||||
const compactSnapshot = compactProviderSnapshot(entries);
|
||||
const snapshotHash = createHash("sha256")
|
||||
.update(JSON.stringify(compactSnapshot))
|
||||
.digest("base64url");
|
||||
return { compactSnapshot, snapshotHash };
|
||||
}
|
||||
|
||||
/**
|
||||
* A client's provider catalog surface: model / mode / feature listing, the providers
|
||||
* snapshot push + pull, provider diagnostics, and usage. The snapshot PUSH (start) and
|
||||
@@ -78,11 +97,25 @@ export class ProviderCatalogSession {
|
||||
this.host.isProviderVisibleToClient(entry.provider),
|
||||
);
|
||||
const snapshotCwd = isGlobalProviderSnapshotKey(cwd) ? undefined : cwd;
|
||||
const clientEntries = this.downgradeEntryModesForClient(visibleEntries);
|
||||
if (this.host.supportsCompactProviderSnapshots()) {
|
||||
const encoded = encodeProviderSnapshot(clientEntries);
|
||||
this.host.emit({
|
||||
type: "providers_snapshot_update",
|
||||
payload: {
|
||||
...(snapshotCwd ? { cwd: snapshotCwd } : {}),
|
||||
entries: [],
|
||||
...encoded,
|
||||
generatedAt: new Date().toISOString(),
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.host.emit({
|
||||
type: "providers_snapshot_update",
|
||||
payload: {
|
||||
...(snapshotCwd ? { cwd: snapshotCwd } : {}),
|
||||
entries: this.downgradeEntryModesForClient(visibleEntries),
|
||||
entries: clientEntries,
|
||||
generatedAt: new Date().toISOString(),
|
||||
},
|
||||
});
|
||||
@@ -360,11 +393,29 @@ export class ProviderCatalogSession {
|
||||
const entries = this.providerSnapshotManager
|
||||
.getSnapshot(msg.cwd ? expandTilde(msg.cwd) : undefined)
|
||||
.filter((entry) => this.host.isProviderVisibleToClient(entry.provider));
|
||||
const clientEntries = this.downgradeEntryModesForClient(entries);
|
||||
|
||||
if (this.host.supportsCompactProviderSnapshots()) {
|
||||
const encoded = encodeProviderSnapshot(clientEntries);
|
||||
const notModified = msg.ifNoneMatch === encoded.snapshotHash;
|
||||
this.host.emit({
|
||||
type: "get_providers_snapshot_response",
|
||||
payload: {
|
||||
entries: [],
|
||||
...(!notModified ? { compactSnapshot: encoded.compactSnapshot } : {}),
|
||||
snapshotHash: encoded.snapshotHash,
|
||||
...(notModified ? { notModified: true } : {}),
|
||||
generatedAt: new Date().toISOString(),
|
||||
requestId: msg.requestId,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.host.emit({
|
||||
type: "get_providers_snapshot_response",
|
||||
payload: {
|
||||
entries: this.downgradeEntryModesForClient(entries),
|
||||
entries: clientEntries,
|
||||
generatedAt: new Date().toISOString(),
|
||||
requestId: msg.requestId,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user