From 0171c12209efa321901c08f62ef64966b849483b Mon Sep 17 00:00:00 2001 From: Kyzcreig <9063726+Kyzcreig@users.noreply.github.com> Date: Fri, 25 Sep 2026 07:27:27 -0700 Subject: [PATCH] audit(scripts+misc): take upstream's web gatewayClient (drop source='dashboard' tagging; 0 dashboard turns in 30d) --- web/src/lib/gatewayClient.test.ts | 52 ------------------------------- web/src/lib/gatewayClient.ts | 30 ------------------ 2 files changed, 82 deletions(-) diff --git a/web/src/lib/gatewayClient.test.ts b/web/src/lib/gatewayClient.test.ts index 86dc5663dec05..5ed55cac5848e 100644 --- a/web/src/lib/gatewayClient.test.ts +++ b/web/src/lib/gatewayClient.test.ts @@ -1,6 +1,5 @@ // @vitest-environment jsdom import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { JsonRpcGatewayClient } from "@hermes/shared"; import { GatewayClient } from "./gatewayClient"; @@ -73,10 +72,6 @@ beforeEach(() => { afterEach(() => { vi.unstubAllGlobals(); - // fork parity NOTE (2026-08-07 upstream merge): the source-tagging suite below - // spies on JsonRpcGatewayClient.prototype, so the shared teardown must also - // restore prototype spies — upstream's teardown only unstubbed globals. - vi.restoreAllMocks(); }); describe("GatewayClient", () => { @@ -99,50 +94,3 @@ describe("GatewayClient", () => { expect(gw.connectionState).toBe("open"); }); }); - -// The dashboard, the Electron desktop app, and the Ink TUI all drive the same -// tui_gateway JSON-RPC server, which historically stamped every turn -// platform="tui". GatewayClient now tags SESSION-ORIGINATING calls with -// source:"dashboard" so the backend attributes browser-dashboard turns to the -// dashboard client (→ blackbox turns.platform → tokens.ace source chart). These -// tests pin that contract by spying on the inherited request(). - -function captureForwarded() { - // Spy on the base-class request so we see exactly what GatewayClient forwards, - // without needing a live WebSocket. - const spy = vi - .spyOn(JsonRpcGatewayClient.prototype, "request") - .mockResolvedValue({} as never); - return spy; -} - -describe("GatewayClient source tagging", () => { - it('stamps source:"dashboard" on session.create', async () => { - const spy = captureForwarded(); - await new GatewayClient().request("session.create", { cols: 96 }); - expect(spy).toHaveBeenCalledWith( - "session.create", - { cols: 96, source: "dashboard" }, - undefined, - undefined, - ); - }); - - it('stamps source:"dashboard" on session.resume', async () => { - const spy = captureForwarded(); - await new GatewayClient().request("session.resume", { session_id: "s1" }); - expect(spy.mock.calls[0][1]).toMatchObject({ session_id: "s1", source: "dashboard" }); - }); - - it("does NOT add source to non-session-originating methods", async () => { - const spy = captureForwarded(); - await new GatewayClient().request("prompt.submit", { session_id: "s1", text: "hi" }); - expect(spy.mock.calls[0][1]).not.toHaveProperty("source"); - }); - - it("never overrides an explicit caller-provided source (e.g. sidebar sidecar tool)", async () => { - const spy = captureForwarded(); - await new GatewayClient().request("session.create", { close_on_disconnect: true, source: "tool" }); - expect(spy.mock.calls[0][1]).toMatchObject({ source: "tool" }); - }); -}); diff --git a/web/src/lib/gatewayClient.ts b/web/src/lib/gatewayClient.ts index 2311dd460cb29..29fd891a27bc1 100644 --- a/web/src/lib/gatewayClient.ts +++ b/web/src/lib/gatewayClient.ts @@ -26,18 +26,6 @@ import { maybeReloadForLoopbackWsAuthFailure } from "@/lib/dashboard-auth-reload export type { ConnectionState, GatewayEvent, GatewayEventName }; -// This client's source label, threaded to the backend on session-originating -// RPCs so turns from the in-browser dashboard chat are attributed to -// "dashboard" rather than the shared "tui" default (the dashboard, the Electron -// desktop app, and the Ink TUI all drive the same JSON-RPC server). The backend -// sanitizes it (see tui_gateway.server._sanitize_client_source). -const CLIENT_SOURCE = "dashboard"; - -// RPC methods that MINT or (RE)ATTACH a session and therefore build an agent -// whose platform should carry the client label. Other methods (steer, title, -// prompt.submit, …) act on an existing session and ignore a `source` field. -const SESSION_ORIGIN_METHODS = new Set(["session.create", "session.resume"]); - export class GatewayClient extends JsonRpcGatewayClient { constructor() { super({ @@ -49,24 +37,6 @@ export class GatewayClient extends JsonRpcGatewayClient { }); } - // Tag session-originating calls with this client so the backend records the - // turn's platform/source as "dashboard" instead of the shared "tui" default. - // Only stamps when the caller hasn't set an explicit source (e.g. the sidebar - // sidecar session deliberately sends source:"tool"), and only on the methods - // that mint/attach a session — every other RPC ignores the field. - request( - method: string, - params: Record = {}, - timeoutMs?: number, - signal?: AbortSignal, - ): Promise { - const outbound = - SESSION_ORIGIN_METHODS.has(method) && params.source === undefined - ? { ...params, source: CLIENT_SOURCE } - : params; - return super.request(method, outbound, timeoutMs, signal); - } - async connect(token?: string): Promise { if (this.connectionState === "open" || this.connectionState === "connecting") { return;