Fix CI + add relay deploy + latency probes

This commit is contained in:
Mohamed Boudra
2026-02-04 23:41:53 +07:00
parent 0218729316
commit 8384b5facf
10 changed files with 116 additions and 45 deletions
@@ -755,6 +755,40 @@ export class DaemonClient {
});
}
async ping(params?: { requestId?: string; timeoutMs?: number }): Promise<{
requestId: string;
clientSentAt: number;
serverReceivedAt: number;
serverSentAt: number;
rttMs: number;
}> {
const requestId =
params?.requestId ??
`ping-${Date.now()}-${Math.random().toString(36).slice(2)}`;
const clientSentAt = Date.now();
const payload = await this.sendRequest({
requestId,
message: { type: "ping", requestId, clientSentAt },
timeout: params?.timeoutMs ?? 5000,
select: (msg) => {
if (msg.type !== "pong") return null;
if (msg.payload.requestId !== requestId) return null;
if (typeof msg.payload.serverReceivedAt !== "number") return null;
if (typeof msg.payload.serverSentAt !== "number") return null;
return msg.payload;
},
});
return {
requestId,
clientSentAt,
serverReceivedAt: payload.serverReceivedAt,
serverSentAt: payload.serverSentAt,
rttMs: Date.now() - clientSentAt,
};
}
// ============================================================================
// Agent RPCs (requestId-correlated)
// ============================================================================
@@ -14,6 +14,8 @@ import { createWorktree, validateBranchSlug } from "../../utils/worktree.js";
const CODEX_TEST_MODEL = "gpt-5.1-codex-mini";
const CODEX_TEST_THINKING_OPTION_ID = "low";
const shouldRun = !process.env.CI && !!process.env.OPENAI_API_KEY;
function tmpCwd(prefix: string): string {
return realpathSync(mkdtempSync(path.join(tmpdir(), prefix)));
}
@@ -36,7 +38,7 @@ function initGitRepo(repoDir: string): void {
});
}
describe("agent metadata generation (real agents)", () => {
(shouldRun ? describe : describe.skip)("agent metadata generation (real agents)", () => {
const logger = pino({ level: "silent" });
let repoDir: string;
let paseoHome: string;
@@ -18,6 +18,11 @@ import pino from "pino";
const CODEX_TEST_MODEL = "gpt-5.1-codex-mini";
const CODEX_TEST_THINKING_OPTION_ID = "low";
const hasOpenAICredentials = !!process.env.OPENAI_API_KEY;
const hasClaudeCredentials =
!!process.env.CLAUDE_SESSION_TOKEN || !!process.env.ANTHROPIC_API_KEY;
const shouldRun = !process.env.CI && (hasOpenAICredentials || hasClaudeCredentials);
type AgentMcpServerHandle = {
url: string;
close: () => Promise<void>;
@@ -136,7 +141,7 @@ async function startAgentMcpServer(logger: pino.Logger): Promise<AgentMcpServerH
};
}
describe("getStructuredAgentResponse (e2e)", () => {
(shouldRun ? describe : describe.skip)("getStructuredAgentResponse (e2e)", () => {
let manager: AgentManager;
let cwd: string;
let agentMcpServer: AgentMcpServerHandle;
@@ -163,7 +168,7 @@ describe("getStructuredAgentResponse (e2e)", () => {
await shutdownProviders(logger);
}, 60000);
test(
test.runIf(hasOpenAICredentials)(
"returns schema-valid JSON from a real Codex agent",
async () => {
const schema = z.object({
@@ -191,7 +196,7 @@ describe("getStructuredAgentResponse (e2e)", () => {
180000
);
test(
test.runIf(hasClaudeCredentials)(
"returns schema-valid JSON from Claude Haiku",
async () => {
const schema = z.object({
@@ -18,6 +18,8 @@ import {
wordSimilarity,
} from "./test-utils/dictation-e2e.js";
const hasOpenAICredentials = !!process.env.OPENAI_API_KEY;
function tmpCwd(): string {
return mkdtempSync(path.join(tmpdir(), "daemon-client-"));
}
@@ -53,11 +55,11 @@ function waitForSignal<T>(
});
}
describe("daemon client E2E", () => {
(hasOpenAICredentials ? describe : describe.skip)("daemon client E2E", () => {
let ctx: DaemonTestContext;
beforeAll(async () => {
const openaiApiKey = requireEnv("OPENAI_API_KEY");
const openaiApiKey = process.env.OPENAI_API_KEY ?? "";
ctx = await createDaemonTestContext({
dictationFinalTimeoutMs: 5000,
openai: { apiKey: openaiApiKey },
+14
View File
@@ -981,6 +981,20 @@ export class Session {
this.handleClientHeartbeat(msg);
break;
case "ping": {
const now = Date.now();
this.emit({
type: "pong",
payload: {
requestId: msg.requestId,
clientSentAt: msg.clientSentAt,
serverReceivedAt: now,
serverSentAt: now,
},
});
break;
}
case "list_commands_request":
await this.handleListCommandsRequest(msg.agentId, msg.requestId);
break;
+18
View File
@@ -787,6 +787,12 @@ export const ClientHeartbeatMessageSchema = z.object({
appVisible: z.boolean(),
});
export const PingMessageSchema = z.object({
type: z.literal("ping"),
requestId: z.string(),
clientSentAt: z.number().int().optional(),
});
export const ListCommandsRequestSchema = z.object({
type: z.literal("list_commands_request"),
agentId: z.string(),
@@ -908,6 +914,7 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
FileDownloadTokenRequestSchema,
ClearAgentAttentionMessageSchema,
ClientHeartbeatMessageSchema,
PingMessageSchema,
ListCommandsRequestSchema,
ExecuteCommandRequestSchema,
RegisterPushTokenMessageSchema,
@@ -1024,6 +1031,16 @@ export const StatusMessageSchema = z.object({
.passthrough(), // Allow additional fields
});
export const PongMessageSchema = z.object({
type: z.literal("pong"),
payload: z.object({
requestId: z.string(),
clientSentAt: z.number().int().optional(),
serverReceivedAt: z.number().int(),
serverSentAt: z.number().int(),
}),
});
export const RpcErrorMessageSchema = z.object({
type: z.literal("rpc_error"),
payload: z.object({
@@ -1617,6 +1634,7 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
DictationStreamFinalMessageSchema,
DictationStreamErrorMessageSchema,
StatusMessageSchema,
PongMessageSchema,
RpcErrorMessageSchema,
InitializeAgentResponseMessageSchema,
ArtifactMessageSchema,