Fix CI + add relay deploy + latency probes
This commit is contained in:
@@ -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 },
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user