From 66a281c4f97058727aa29b755140edcf7dff88af Mon Sep 17 00:00:00 2001 From: scoootscooob Date: Mon, 6 Apr 2026 15:35:36 -0700 Subject: [PATCH] gateway: fix compaction regression coverage --- src/gateway/server-methods/sessions.ts | 13 ++++---- ...sessions.gateway-server-sessions-a.test.ts | 32 +++++++++++++++++-- .../session-compaction-checkpoints.test.ts | 32 +++++++++++++++++-- src/gateway/test-helpers.runtime-state.ts | 12 +++++++ ui/src/ui/controllers/sessions.test.ts | 5 +++ ui/src/ui/views/sessions.test.ts | 8 +++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/src/gateway/server-methods/sessions.ts b/src/gateway/server-methods/sessions.ts index ed1d1fad6d3..10e85c19b0f 100644 --- a/src/gateway/server-methods/sessions.ts +++ b/src/gateway/server-methods/sessions.ts @@ -9,6 +9,7 @@ import { waitForEmbeddedPiRunEnd, } from "../../agents/pi-embedded-runner/runs.js"; import { compactEmbeddedPiSession } from "../../agents/pi-embedded.js"; +import { normalizeReasoningLevel, normalizeThinkLevel } from "../../auto-reply/thinking.js"; import { clearSessionQueues } from "../../auto-reply/reply/queue/cleanup.js"; import { loadConfig } from "../../config/config.js"; import { @@ -1464,7 +1465,7 @@ export const sessionsHandlers: GatewayRequestHandlers = { const messages = limit < allMessages.length ? allMessages.slice(-limit) : allMessages; respond(true, { messages }, undefined); }, - "sessions.compact": async ({ params, respond, context }) => { + "sessions.compact": async ({ req, params, respond, context, client, isWebchatConnect }) => { if (!assertValidParams(params, validateSessionsCompactParams, "sessions.compact", respond)) { return; } @@ -1523,10 +1524,10 @@ export const sessionsHandlers: GatewayRequestHandlers = { if (maxLines === undefined) { const interruptResult = await interruptSessionRunIfActive({ - req: undefined, + req, context, - client: null, - isWebchatConnect: () => false, + client, + isWebchatConnect, requestedKey: key, canonicalKey: target.canonicalKey, sessionId, @@ -1548,8 +1549,8 @@ export const sessionsHandlers: GatewayRequestHandlers = { config: cfg, provider: resolvedModel.provider, model: resolvedModel.model, - thinkLevel: entry?.thinkingLevel, - reasoningLevel: entry?.reasoningLevel, + thinkLevel: normalizeThinkLevel(entry?.thinkingLevel), + reasoningLevel: normalizeReasoningLevel(entry?.reasoningLevel), bashElevated: { enabled: false, allowed: false, diff --git a/src/gateway/server.sessions.gateway-server-sessions-a.test.ts b/src/gateway/server.sessions.gateway-server-sessions-a.test.ts index cc1ec6d9243..6a04155f96a 100644 --- a/src/gateway/server.sessions.gateway-server-sessions-a.test.ts +++ b/src/gateway/server.sessions.gateway-server-sessions-a.test.ts @@ -2,6 +2,7 @@ import fsSync from "node:fs"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +import type { AssistantMessage, UserMessage } from "@mariozechner/pi-ai"; import { SessionManager } from "@mariozechner/pi-coding-agent"; import { afterAll, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import { WebSocket } from "ws"; @@ -225,11 +226,36 @@ async function writeSingleLineSession(dir: string, sessionId: string, content: s function createCheckpointFixture(dir: string) { const session = SessionManager.create(dir, dir); - session.appendMessage({ role: "user", content: "before compaction" }); - session.appendMessage({ + const userMessage: UserMessage = { + role: "user", + content: "before compaction", + timestamp: Date.now(), + }; + const assistantMessage: AssistantMessage = { role: "assistant", content: [{ type: "text", text: "working on it" }], - }); + api: "responses", + provider: "openai", + model: "gpt-test", + usage: { + input: 1, + output: 1, + cacheRead: 0, + cacheWrite: 0, + totalTokens: 2, + cost: { + input: 0, + output: 0, + cacheRead: 0, + cacheWrite: 0, + total: 0, + }, + }, + stopReason: "stop", + timestamp: Date.now(), + }; + session.appendMessage(userMessage); + session.appendMessage(assistantMessage); const preCompactionLeafId = session.getLeafId(); if (!preCompactionLeafId) { throw new Error("expected persisted session leaf before compaction"); diff --git a/src/gateway/session-compaction-checkpoints.test.ts b/src/gateway/session-compaction-checkpoints.test.ts index 485dd1b7931..63a64804bfe 100644 --- a/src/gateway/session-compaction-checkpoints.test.ts +++ b/src/gateway/session-compaction-checkpoints.test.ts @@ -2,6 +2,7 @@ import fsSync from "node:fs"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +import type { AssistantMessage, UserMessage } from "@mariozechner/pi-ai"; import { SessionManager } from "@mariozechner/pi-coding-agent"; import { afterEach, describe, expect, test } from "vitest"; import { @@ -21,11 +22,36 @@ describe("session-compaction-checkpoints", () => { tempDirs.push(dir); const session = SessionManager.create(dir, dir); - session.appendMessage({ role: "user", content: "before compaction" }); - session.appendMessage({ + const userMessage: UserMessage = { + role: "user", + content: "before compaction", + timestamp: Date.now(), + }; + const assistantMessage: AssistantMessage = { role: "assistant", content: [{ type: "text", text: "working on it" }], - }); + api: "responses", + provider: "openai", + model: "gpt-test", + usage: { + input: 1, + output: 1, + cacheRead: 0, + cacheWrite: 0, + totalTokens: 2, + cost: { + input: 0, + output: 0, + cacheRead: 0, + cacheWrite: 0, + total: 0, + }, + }, + stopReason: "stop", + timestamp: Date.now(), + }; + session.appendMessage(userMessage); + session.appendMessage(assistantMessage); const sessionFile = session.getSessionFile(); const leafId = session.getLeafId(); diff --git a/src/gateway/test-helpers.runtime-state.ts b/src/gateway/test-helpers.runtime-state.ts index b509801cbcf..01a2177ee45 100644 --- a/src/gateway/test-helpers.runtime-state.ts +++ b/src/gateway/test-helpers.runtime-state.ts @@ -22,6 +22,7 @@ export type AgentCommandFn = (...args: unknown[]) => Promise; export type SendWhatsAppFn = (...args: unknown[]) => Promise<{ messageId: string; toJid: string }>; export type RunBtwSideQuestionFn = (...args: unknown[]) => Promise; export type DispatchInboundMessageFn = (...args: unknown[]) => Promise; +export type CompactEmbeddedPiSessionFn = (...args: unknown[]) => Promise; const GATEWAY_TEST_CONFIG_ROOT_KEY = Symbol.for("openclaw.gatewayTestHelpers.configRoot"); @@ -49,6 +50,7 @@ export type GatewayTestHoistedState = { abortCalls: string[]; waitCalls: string[]; waitResults: Map; + compactEmbeddedPiSession: Mock; }; testTailscaleWhois: { value: TailscaleWhoisIdentity | null }; getReplyFromConfig: Mock; @@ -99,6 +101,16 @@ const gatewayTestHoisted = vi.hoisted(() => { abortCalls: [], waitCalls: [], waitResults: new Map(), + compactEmbeddedPiSession: vi.fn().mockResolvedValue({ + ok: true, + compacted: true, + result: { + summary: "summary", + firstKeptEntryId: "entry-1", + tokensBefore: 120, + tokensAfter: 80, + }, + }), }, testTailscaleWhois: { value: null }, getReplyFromConfig: vi.fn().mockResolvedValue(undefined), diff --git a/ui/src/ui/controllers/sessions.test.ts b/ui/src/ui/controllers/sessions.test.ts index ad7d8f56485..5b8a21da957 100644 --- a/ui/src/ui/controllers/sessions.test.ts +++ b/ui/src/ui/controllers/sessions.test.ts @@ -22,6 +22,11 @@ function createState(request: RequestFn, overrides: Partial = {}) sessionsFilterLimit: "0", sessionsIncludeGlobal: true, sessionsIncludeUnknown: true, + sessionsExpandedCheckpointKey: null, + sessionsCheckpointItemsByKey: {}, + sessionsCheckpointLoadingKey: null, + sessionsCheckpointBusyKey: null, + sessionsCheckpointErrorByKey: {}, ...overrides, }; } diff --git a/ui/src/ui/views/sessions.test.ts b/ui/src/ui/views/sessions.test.ts index 0f18a179b1b..62367dfc2c9 100644 --- a/ui/src/ui/views/sessions.test.ts +++ b/ui/src/ui/views/sessions.test.ts @@ -41,6 +41,11 @@ function buildProps(result: SessionsListResult): SessionsProps { page: 0, pageSize: 10, selectedKeys: new Set(), + expandedCheckpointKey: null, + checkpointItemsByKey: {}, + checkpointLoadingKey: null, + checkpointBusyKey: null, + checkpointErrorByKey: {}, onFiltersChange: () => undefined, onSearchChange: () => undefined, onSortChange: () => undefined, @@ -53,6 +58,9 @@ function buildProps(result: SessionsListResult): SessionsProps { onDeselectPage: () => undefined, onDeselectAll: () => undefined, onDeleteSelected: () => undefined, + onToggleCheckpointDetails: () => undefined, + onBranchFromCheckpoint: () => undefined, + onRestoreCheckpoint: () => undefined, }; }