Merge remote-tracking branch 'origin/main' into meow/diagnostic-liveness-stability

This commit is contained in:
Val Alexander
2026-05-11 06:58:56 -05:00
9 changed files with 93 additions and 96 deletions
+25 -16
View File
@@ -169,21 +169,22 @@ describe("ACP translator event ledger replay", () => {
await secondAgent.loadSession(createLoadSessionRequest(created.sessionId));
expect(secondRequestMock).not.toHaveBeenCalledWith("sessions.get", expect.anything());
expect(secondRequestMock.mock.calls.map((call) => call[0])).not.toContain("sessions.get");
const replayedUpdates = secondConnection.__sessionUpdateMock.mock.calls.map(
(call) => call[0]?.update,
);
const replayedUpdateTypes = replayedUpdates.map((update) => update?.sessionUpdate);
expect(replayedUpdateTypes).toEqual(
expect.arrayContaining([
"session_info_update",
"available_commands_update",
"user_message_chunk",
"tool_call",
"tool_call_update",
"agent_message_chunk",
]),
);
expect(replayedUpdateTypes).toEqual([
"session_info_update",
"available_commands_update",
"user_message_chunk",
"tool_call",
"tool_call_update",
"agent_message_chunk",
"session_info_update",
"session_info_update",
"available_commands_update",
]);
expect(replayedUpdates).toContainEqual({
sessionUpdate: "user_message_chunk",
content: { type: "text", text: "Question" },
@@ -223,13 +224,21 @@ describe("ACP translator event ledger replay", () => {
await listedAgent.loadSession(createLoadSessionRequest(firstSession.sessionKey));
expect(listedRequestMock).not.toHaveBeenCalledWith("sessions.get", expect.anything());
expect(listedRequestMock.mock.calls.map((call) => call[0])).not.toContain("sessions.get");
const listedReplayTypes = listedConnection.__sessionUpdateMock.mock.calls.map(
(call) => call[0]?.update?.sessionUpdate,
);
expect(listedReplayTypes).toEqual(
expect.arrayContaining(["user_message_chunk", "tool_call", "agent_message_chunk"]),
);
expect(listedReplayTypes).toEqual([
"session_info_update",
"available_commands_update",
"user_message_chunk",
"tool_call",
"tool_call_update",
"agent_message_chunk",
"session_info_update",
"session_info_update",
"available_commands_update",
]);
const listedPrompt = listedAgent.prompt(
createPromptRequest(firstSession.sessionKey, "Follow-up"),
@@ -265,7 +274,7 @@ describe("ACP translator event ledger replay", () => {
).toHaveLength(2);
await expect(
eventLedger.readReplayBySessionId({ sessionId: firstSession.sessionKey }),
).resolves.toMatchObject({ complete: false });
).resolves.toEqual({ complete: false, events: [] });
firstSessionStore.clearAllSessionsForTest();
});
+5 -5
View File
@@ -574,9 +574,9 @@ describe("followup queue collect routing", () => {
await done.promise;
expect(calls.map((call) => call.prompt)).toEqual([
expect.stringContaining("first"),
expect.stringContaining("second"),
expect.stringContaining("third"),
"[Queued messages while agent was busy]\n\n---\nQueued #1 (from A)\nfirst",
"[Queued messages while agent was busy]\n\n---\nQueued #1 (from Owner)\nsecond",
"[Queued messages while agent was busy]\n\n---\nQueued #1 (from A)\nthird",
]);
});
@@ -814,8 +814,8 @@ describe("followup queue collect routing", () => {
expect(guestAttempts).toHaveLength(1);
expect(ownerAttempts).toHaveLength(2);
expect(successfulCalls.map((call) => call.prompt)).toEqual([
expect.stringContaining("guest message"),
expect.stringContaining("owner message"),
"[Queued messages while agent was busy]\n\n---\nQueued #1 (from Guest)\nguest message",
"[Queued messages while agent was busy]\n\n---\nQueued #1 (from Owner)\nowner message",
]);
});
+2 -2
View File
@@ -94,7 +94,7 @@ describe("completion-cli write-state", () => {
await program.parseAsync(["completion", "--write-state"], { from: "user" });
const cacheDir = path.join(stateDir, "completions");
expect((await fs.readdir(cacheDir)).sort()).toEqual([
expect((await fs.readdir(cacheDir)).toSorted()).toEqual([
"openclaw.bash",
"openclaw.fish",
"openclaw.ps1",
@@ -137,7 +137,7 @@ describe("completion-cli write-state", () => {
[program, "qa", process.argv, { purpose: "completion" }],
]);
expect(registerPluginCliCommandsFromValidatedConfigMock).not.toHaveBeenCalled();
expect((await fs.readdir(path.join(stateDir, "completions"))).sort()).toEqual([
expect((await fs.readdir(path.join(stateDir, "completions"))).toSorted()).toEqual([
"openclaw.bash",
"openclaw.fish",
"openclaw.ps1",
+19 -25
View File
@@ -570,15 +570,16 @@ describe("runCli exit behavior", () => {
["tools", ["node", "openclaw", "tools", "effective"]],
])("keeps reserved %s command roots out of plugin command discovery", async (_name, argv) => {
const parseAsync = vi.fn().mockResolvedValueOnce(undefined);
buildProgramMock.mockReturnValueOnce({
const program = {
commands: [],
parseAsync,
});
};
buildProgramMock.mockReturnValueOnce(program);
await runCli(argv);
expect(startProxyMock).not.toHaveBeenCalled();
expect(registerSubCliByNameMock).toHaveBeenCalledWith(expect.anything(), argv[2], argv);
expect(registerSubCliByNameMock.mock.calls).toEqual([[program, argv[2], argv]]);
expect(registerPluginCliCommandsFromValidatedConfigMock).not.toHaveBeenCalled();
expect(parseAsync).toHaveBeenCalledWith(argv);
});
@@ -801,47 +802,41 @@ describe("runCli exit behavior", () => {
it("swallows Commander parse exits after recording the exit code", async () => {
const exitCode = process.exitCode;
buildProgramMock.mockReturnValueOnce({
const program = {
commands: [{ name: () => "status" }],
parseAsync: vi
.fn()
.mockRejectedValueOnce(
new CommanderError(1, "commander.excessArguments", "too many arguments for 'status'"),
),
});
};
buildProgramMock.mockReturnValueOnce(program);
await expect(runCli(["node", "openclaw", "status"])).resolves.toBeUndefined();
expect(registerSubCliByNameMock).toHaveBeenCalledWith(expect.anything(), "status", [
"node",
"openclaw",
"status",
expect(registerSubCliByNameMock.mock.calls).toEqual([
[program, "status", ["node", "openclaw", "status"]],
]);
expect(process.exitCode).toBe(1);
process.exitCode = exitCode;
});
it("loads the real primary command before rendering command help", async () => {
buildProgramMock.mockReturnValueOnce({
const program = {
commands: [{ name: () => "doctor" }],
parseAsync: vi.fn().mockResolvedValueOnce(undefined),
});
};
buildProgramMock.mockReturnValueOnce(program);
const ctx = { programVersion: "0.0.0-test" };
getProgramContextMock.mockReturnValueOnce(ctx as never);
await runCli(["node", "openclaw", "doctor", "--help"]);
expect(registerCoreCliByNameMock).toHaveBeenCalledWith(expect.anything(), ctx, "doctor", [
"node",
"openclaw",
"doctor",
"--help",
expect(registerCoreCliByNameMock.mock.calls).toEqual([
[program, ctx, "doctor", ["node", "openclaw", "doctor", "--help"]],
]);
expect(registerSubCliByNameMock).toHaveBeenCalledWith(expect.anything(), "doctor", [
"node",
"openclaw",
"doctor",
"--help",
expect(registerSubCliByNameMock.mock.calls).toEqual([
[program, "doctor", ["node", "openclaw", "doctor", "--help"]],
]);
});
@@ -907,10 +902,9 @@ describe("runCli exit behavior", () => {
code: "EHOSTUNREACH",
});
expect(handler(hostUnreachable)).toBeUndefined();
expect(consoleWarnSpy).toHaveBeenCalledWith(
"[openclaw] Non-fatal uncaught exception (continuing):",
expect.stringContaining("EHOSTUNREACH"),
);
expect(consoleWarnSpy.mock.calls).toEqual([
["[openclaw] Non-fatal uncaught exception (continuing):", hostUnreachable.stack],
]);
expect(restoreTerminalStateMock).not.toHaveBeenCalled();
expect(exitSpy).not.toHaveBeenCalled();
} finally {
+2 -4
View File
@@ -124,10 +124,8 @@ describe("gateway stability lane", () => {
expect(lastSeq).toBeGreaterThan(firstSeq);
expect(snapshot.summary.byType["diagnostic.memory.sample"]).toBeGreaterThan(0);
expect(snapshot.summary.byType["message.queued"]).toBeGreaterThan(0);
expect(snapshot.summary.memory).toMatchObject({
maxRssBytes: maxSyntheticRssBytes,
pressureCount: 0,
});
expect(snapshot.summary.memory?.maxRssBytes).toBe(maxSyntheticRssBytes);
expect(snapshot.summary.memory?.pressureCount).toBe(0);
expect(snapshot.summary.memory?.maxHeapUsedBytes).toBeLessThan(96 * MB);
expect(snapshot.summary.payloadLarge?.chunked).toBeGreaterThan(0);
expect(snapshot.summary.payloadLarge?.bySurface["gateway.stability.probe"]).toBeGreaterThan(0);
+4 -2
View File
@@ -58,10 +58,11 @@ describe("openresponses phase support", () => {
phase: "commentary",
status: "completed",
}),
).toMatchObject({
).toEqual({
type: "message",
id: "msg_commentary",
role: "assistant",
content: [{ type: "output_text", text: "Checking logs." }],
phase: "commentary",
status: "completed",
});
@@ -73,10 +74,11 @@ describe("openresponses phase support", () => {
phase: "final_answer",
status: "completed",
}),
).toMatchObject({
).toEqual({
type: "message",
id: "msg_final",
role: "assistant",
content: [{ type: "output_text", text: "Root cause found." }],
phase: "final_answer",
status: "completed",
});
+13 -11
View File
@@ -1,8 +1,10 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
type StartGatewayDiscovery = typeof import("./server-discovery-runtime.js").startGatewayDiscovery;
const mocks = vi.hoisted(() => ({
getMachineDisplayName: vi.fn(async () => "Test Machine"),
startGatewayDiscovery: vi.fn(async () => ({ bonjourStop: null })),
startGatewayDiscovery: vi.fn<StartGatewayDiscovery>(async () => ({ bonjourStop: null })),
}));
vi.mock("../infra/machine-name.js", () => ({
@@ -87,15 +89,15 @@ describe("startGatewayEarlyRuntime", () => {
}),
).resolves.toBe(stop);
expect(mocks.startGatewayDiscovery).toHaveBeenCalledWith(
expect.objectContaining({
machineDisplayName: "Test Machine",
port: 19_001,
gatewayTls: { enabled: true, fingerprintSha256: "abc123" },
tailscaleMode: "serve",
mdnsMode: "full",
gatewayDiscoveryServices: [service],
}),
);
const [discoveryParams] = mocks.startGatewayDiscovery.mock.calls.at(-1) ?? [];
if (discoveryParams === undefined) {
throw new Error("Expected gateway discovery to start");
}
expect(discoveryParams.machineDisplayName).toBe("Test Machine");
expect(discoveryParams.port).toBe(19_001);
expect(discoveryParams.gatewayTls).toEqual({ enabled: true, fingerprintSha256: "abc123" });
expect(discoveryParams.tailscaleMode).toBe("serve");
expect(discoveryParams.mdnsMode).toBe("full");
expect(discoveryParams.gatewayDiscoveryServices).toEqual([service]);
});
});
+15 -20
View File
@@ -36,6 +36,19 @@ vi.mock("../agents/pi-embedded-runner/runtime.js", () => ({
let prewarmConfiguredPrimaryModel: typeof import("./server-startup-post-attach.js").__testing.prewarmConfiguredPrimaryModel;
let shouldSkipStartupModelPrewarm: typeof import("./server-startup-post-attach.js").__testing.shouldSkipStartupModelPrewarm;
function expectModelsJsonPrewarmCall(cfg: OpenClawConfig) {
expect(ensureOpenClawModelsJsonMock).toHaveBeenCalledTimes(1);
const [calledConfig, agentDir, options] = ensureOpenClawModelsJsonMock.mock.calls[0] ?? [];
expect(calledConfig).toBe(cfg);
expect(agentDir).toBe("/tmp/agent");
expect(options).toEqual({
workspaceDir: "/tmp/workspace",
providerDiscoveryProviderIds: ["openai-codex"],
providerDiscoveryTimeoutMs: 5000,
providerDiscoveryEntriesOnly: true,
});
}
describe("gateway startup primary model warmup", () => {
beforeAll(async () => {
({
@@ -66,16 +79,7 @@ describe("gateway startup primary model warmup", () => {
log: { warn: vi.fn() },
});
expect(ensureOpenClawModelsJsonMock).toHaveBeenCalledWith(
cfg,
"/tmp/agent",
expect.objectContaining({
workspaceDir: "/tmp/workspace",
providerDiscoveryProviderIds: ["openai-codex"],
providerDiscoveryTimeoutMs: 5000,
providerDiscoveryEntriesOnly: true,
}),
);
expectModelsJsonPrewarmCall(cfg);
expect(piModelModuleLoadedMock).not.toHaveBeenCalled();
});
@@ -163,16 +167,7 @@ describe("gateway startup primary model warmup", () => {
log: { warn: vi.fn() },
});
expect(ensureOpenClawModelsJsonMock).toHaveBeenCalledWith(
cfg,
"/tmp/agent",
expect.objectContaining({
workspaceDir: "/tmp/workspace",
providerDiscoveryProviderIds: ["openai-codex"],
providerDiscoveryTimeoutMs: 5000,
providerDiscoveryEntriesOnly: true,
}),
);
expectModelsJsonPrewarmCall(cfg);
expect(piModelModuleLoadedMock).not.toHaveBeenCalled();
});
+8 -11
View File
@@ -69,22 +69,19 @@ test("sessions.create stores dashboard session model and parent linkage, and cre
}
>;
const key = created.payload?.key as string;
expect(rawStore[key]).toMatchObject({
sessionId: created.payload?.sessionId,
label: "Dashboard Chat",
providerOverride: "openai",
modelOverride: "gpt-test-a",
parentSessionKey: "agent:main:main",
});
expect(rawStore[key]?.sessionId).toBe(created.payload?.sessionId);
expect(rawStore[key]?.label).toBe("Dashboard Chat");
expect(rawStore[key]?.providerOverride).toBe("openai");
expect(rawStore[key]?.modelOverride).toBe("gpt-test-a");
expect(rawStore[key]?.parentSessionKey).toBe("agent:main:main");
expect(sessionFile).toBe(rawStore[key]?.sessionFile);
const transcriptPath = path.join(dir, `${created.payload?.sessionId}.jsonl`);
const transcript = await fs.readFile(transcriptPath, "utf-8");
const [headerLine] = transcript.trim().split(/\r?\n/, 1);
expect(JSON.parse(headerLine) as { type?: string; id?: string }).toMatchObject({
type: "session",
id: created.payload?.sessionId,
});
const header = JSON.parse(headerLine) as { type?: string; id?: string };
expect(header.type).toBe("session");
expect(header.id).toBe(created.payload?.sessionId);
});
test("sessions.create accepts an explicit key for persistent dashboard sessions", async () => {