From 5d18edd31e56145911eb875a103eba181a3f4888 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sat, 11 Jul 2026 12:00:42 +0200 Subject: [PATCH] Verify real agent workflows through the hosted relay (#1998) * test(relay): prove live daemon client workflow * test(relay): reduce live acceptance variance * test(relay): run acceptance through real codex --- packages/server/package.json | 1 + .../daemon-e2e/live-relay.real.e2e.test.ts | 112 ++++++++++++++++++ .../src/server/test-utils/paseo-daemon.ts | 4 + 3 files changed, 117 insertions(+) create mode 100644 packages/server/src/server/daemon-e2e/live-relay.real.e2e.test.ts diff --git a/packages/server/package.json b/packages/server/package.json index dd769f34f..5b46ac5d5 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -60,6 +60,7 @@ "test:e2e:all": "vitest run e2e.test.ts --maxWorkers=1", "test:e2e:real": "npm run test:integration:real", "test:e2e:local": "npm run test:integration:local", + "test:e2e:relay:live": "cross-env RUN_LIVE_RELAY_E2E=1 vitest run src/server/daemon-e2e/live-relay.real.e2e.test.ts --maxWorkers=1", "test:e2e:ui": "vitest --ui e2e.test.ts" }, "dependencies": { diff --git a/packages/server/src/server/daemon-e2e/live-relay.real.e2e.test.ts b/packages/server/src/server/daemon-e2e/live-relay.real.e2e.test.ts new file mode 100644 index 000000000..926f81080 --- /dev/null +++ b/packages/server/src/server/daemon-e2e/live-relay.real.e2e.test.ts @@ -0,0 +1,112 @@ +import { afterEach, describe, expect, test } from "vitest"; +import pino from "pino"; + +import { DaemonClient } from "../test-utils/daemon-client.js"; +import { createTestPaseoDaemon, type TestPaseoDaemon } from "../test-utils/paseo-daemon.js"; +import { generateLocalPairingOffer } from "../pairing-offer.js"; +import { CodexAppServerAgentClient } from "../agent/providers/codex-app-server-agent.js"; +import { buildRelayWebSocketUrl } from "@getpaseo/protocol/daemon-endpoints"; +import { + parseConnectionOfferFromUrl, + type ConnectionOffer, +} from "@getpaseo/protocol/connection-offer"; + +const relayEndpoint = process.env.PASEO_LIVE_RELAY_ENDPOINT ?? "paseo-relay-next.fly.dev:443"; +const liveTest = process.env.RUN_LIVE_RELAY_E2E === "1" ? test : test.skip; + +function requireOffer(url: string): ConnectionOffer { + const offer = parseConnectionOfferFromUrl(url); + if (!offer) { + throw new Error("Pairing did not produce a relay connection offer"); + } + return offer; +} + +async function pairingOfferFor(daemon: TestPaseoDaemon): Promise { + const pairing = await generateLocalPairingOffer({ + paseoHome: daemon.paseoHome, + relayEnabled: true, + relayEndpoint, + relayPublicEndpoint: relayEndpoint, + relayUseTls: true, + relayPublicUseTls: true, + includeQr: false, + }); + if (!pairing.url) { + throw new Error("Pairing did not produce a URL"); + } + return requireOffer(pairing.url); +} + +function clientFor(offer: ConnectionOffer): DaemonClient { + return new DaemonClient({ + url: buildRelayWebSocketUrl({ + endpoint: offer.relay.endpoint, + useTls: true, + serverId: offer.serverId, + role: "client", + }), + clientId: "clid_live_relay_acceptance", + clientType: "cli", + connectTimeoutMs: 30_000, + e2ee: { enabled: true, daemonPublicKeyB64: offer.daemonPublicKeyB64 }, + reconnect: { enabled: false }, + }); +} + +describe("live hosted relay", () => { + let daemon: TestPaseoDaemon | null = null; + let client: DaemonClient | null = null; + + afterEach(async () => { + await client?.close().catch(() => undefined); + await daemon?.close(); + }); + + liveTest( + "carries a complete DaemonClient agent workflow through the hosted relay", + async () => { + const logger = pino({ level: "silent" }); + daemon = await createTestPaseoDaemon({ + listen: "127.0.0.1", + relayEnabled: true, + relayEndpoint, + relayUseTls: true, + agentClients: { codex: new CodexAppServerAgentClient(logger) }, + logger, + }); + const offer = await pairingOfferFor(daemon); + client = clientFor(offer); + + await client.connect(); + const initialAgents = await client.fetchAgents(); + const agent = await client.createAgent({ + provider: "codex", + cwd: daemon.staticDir, + title: "Live relay acceptance", + modeId: "full-access", + }); + await client.sendMessage(agent.id, "Respond with exactly: RELAY_ACCEPTANCE_OK"); + const finished = await client.waitForFinish(agent.id, 120_000); + const timeline = await client.fetchAgentTimeline(agent.id, { + direction: "tail", + limit: 20, + projection: "canonical", + }); + const assistantText = timeline.entries + .filter((entry) => entry.item.type === "assistant_message") + .map((entry) => entry.item.text) + .join(""); + + expect(initialAgents.entries).toEqual([]); + expect(agent).toMatchObject({ + provider: "codex", + cwd: daemon.staticDir, + status: "idle", + }); + expect(finished).toMatchObject({ status: "idle" }); + expect(assistantText).toMatch(/^RELAY_ACCEPTANCE_OK\s*$/); + }, + 180_000, + ); +}); diff --git a/packages/server/src/server/test-utils/paseo-daemon.ts b/packages/server/src/server/test-utils/paseo-daemon.ts index 58c2ebd9b..8d4006560 100644 --- a/packages/server/src/server/test-utils/paseo-daemon.ts +++ b/packages/server/src/server/test-utils/paseo-daemon.ts @@ -23,6 +23,8 @@ interface TestPaseoDaemonOptions { isDev?: boolean; relayEnabled?: boolean; relayEndpoint?: string; + relayUseTls?: boolean; + relayPublicUseTls?: boolean; agentClients?: Partial>; providerOverrides?: PaseoDaemonConfig["providerOverrides"]; paseoHomeRoot?: string; @@ -166,6 +168,8 @@ async function prepareTestDaemonConfig( agentStoragePath: path.join(paseoHome, "agents"), relayEnabled: options.relayEnabled ?? false, relayEndpoint: options.relayEndpoint ?? "relay.paseo.sh:443", + relayUseTls: options.relayUseTls, + relayPublicUseTls: options.relayPublicUseTls, appBaseUrl: "https://app.paseo.sh", auth: options.auth, pushNotificationSender: options.pushNotificationSender,