refactor(session-catalog): normalize search at gateway (#108240)

Co-authored-by: Leon-SK668 <0668001470@xydigit.com>
This commit is contained in:
Peter Steinberger
2026-07-15 03:38:05 -07:00
committed by GitHub
parent 54f70c5d8c
commit 026b688161
7 changed files with 54 additions and 108 deletions
@@ -16,7 +16,6 @@ import type {
SessionsCatalogReadResult,
} from "openclaw/plugin-sdk/session-catalog";
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
import {
OPENCODE_LOCAL_SESSION_HOST_ID as LOCAL_HOST_ID,
OPENCODE_NODE_INVOKE_TIMEOUT_MS as NODE_TIMEOUT_MS,
@@ -46,7 +45,6 @@ export {
const MAX_HOSTS = 100;
const MAX_CURSOR_LENGTH = 128;
const MAX_SEARCH_LENGTH = 500;
const TRANSCRIPT_ITEM_TYPES = new Set([
"userMessage",
"agentMessage",
@@ -244,9 +242,7 @@ async function listOpenCodeNodeHost(
command: OPENCODE_SESSIONS_LIST_COMMAND,
params: {
...(query.limitPerHost ? { limit: query.limitPerHost } : {}),
...(query.search?.trim()
? { searchTerm: truncateUtf16Safe(query.search.trim(), MAX_SEARCH_LENGTH) }
: {}),
...(query.search ? { searchTerm: query.search } : {}),
...(query.cursors?.[hostId] ? { cursor: query.cursors[hostId] } : {}),
},
timeoutMs: NODE_TIMEOUT_MS,
@@ -317,9 +313,6 @@ async function listOpenCodeHosts(
query: Parameters<SessionCatalogProvider["list"]>[0],
): Promise<SessionCatalogHost[]> {
const requested = query.hostIds ? new Set(query.hostIds) : undefined;
const searchTerm = query.search
? truncateUtf16Safe(query.search.trim(), MAX_SEARCH_LENGTH) || undefined
: undefined;
const hosts: SessionCatalogHost[] = [];
if (
(!requested || requested.has(LOCAL_HOST_ID)) &&
@@ -337,7 +330,7 @@ async function listOpenCodeHosts(
connected: true,
...(await listLocalOpenCodeSessionPage({
limit: query.limitPerHost,
...(searchTerm ? { searchTerm } : {}),
...(query.search ? { searchTerm: query.search } : {}),
cursor: query.cursors?.[LOCAL_HOST_ID],
}).then((page) => setTerminalCapability(page, true))),
});
+4 -46
View File
@@ -46,10 +46,6 @@ import {
readLocalOpenCodeTranscriptPage,
} from "./session-catalog.js";
const UTF16_SEARCH_PREFIX = "x".repeat(499);
const UTF16_BOUNDARY_SEARCH = `${UTF16_SEARCH_PREFIX}😀`;
const LONE_SURROGATE_PATTERN =
/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/u;
const temporaryDirectories: string[] = [];
const originalPath = process.env.PATH;
const originalUnrelatedEnv = process.env.CATALOG_UNRELATED_ENV;
@@ -201,41 +197,9 @@ describe("OpenCode session catalog", () => {
await expect(
provider!.read({ hostId: "gateway", threadId: "ses_test", limit: 2 }),
).resolves.toMatchObject({ threadId: "ses_test", items: expect.any(Array) });
await expect(provider!.list({ search: " " })).resolves.toEqual([
await expect(provider!.list({})).resolves.toEqual([
expect.objectContaining({ hostId: "gateway", sessions: [expect.any(Object)] }),
]);
await expect(provider!.list({ search: "x".repeat(501) })).resolves.toEqual([
expect.objectContaining({ hostId: "gateway", sessions: [] }),
]);
},
);
it.runIf(process.platform !== "win32")(
"keeps local search needles UTF-16 safe at the length limit",
async () => {
await installFakeOpenCode("hi", UTF16_SEARCH_PREFIX);
let provider: Parameters<OpenClawPluginApi["registerSessionCatalog"]>[0] | undefined;
registerOpenCodeSessionCatalog({
pluginConfig: {},
runtime: { nodes: { list: vi.fn().mockResolvedValue({ nodes: [] }) } },
registerSessionCatalog: (value: NonNullable<typeof provider>) => {
provider = value;
},
registerNodeHostCommand: vi.fn(),
registerNodeInvokePolicy: vi.fn(),
} as unknown as OpenClawPluginApi);
await expect(
provider!.list({ hostIds: ["gateway"], search: UTF16_BOUNDARY_SEARCH }),
).resolves.toEqual([
expect.objectContaining({
hostId: "gateway",
sessions: [expect.objectContaining({ threadId: "ses_test", name: UTF16_SEARCH_PREFIX })],
}),
]);
await expect(
provider!.list({ hostIds: ["gateway"], search: `${"y".repeat(499)}😀` }),
).resolves.toEqual([expect.objectContaining({ hostId: "gateway", sessions: [] })]);
},
);
@@ -411,9 +375,7 @@ describe("OpenCode session catalog", () => {
registerNodeInvokePolicy: vi.fn(),
} as unknown as OpenClawPluginApi);
await expect(
provider!.list({ hostIds: ["node:node-1"], search: UTF16_BOUNDARY_SEARCH }),
).resolves.toEqual([
await expect(provider!.list({ hostIds: ["node:node-1"], search: "remote" })).resolves.toEqual([
expect.objectContaining({
sessions: [expect.objectContaining({ threadId: "ses_remote", canOpenTerminal: true })],
}),
@@ -421,14 +383,10 @@ describe("OpenCode session catalog", () => {
expect(invoke).toHaveBeenNthCalledWith(1, {
nodeId: "node-1",
command: OPENCODE_SESSIONS_LIST_COMMAND,
params: { searchTerm: UTF16_SEARCH_PREFIX },
params: { searchTerm: "remote" },
timeoutMs: 35_000,
scopes: ["operator.write"],
});
const firstRequest = invoke.mock.calls[0]?.[0] as
| { params?: { searchTerm?: string } }
| undefined;
expect(firstRequest?.params?.searchTerm).not.toMatch(LONE_SURROGATE_PATTERN);
await expect(
provider!.openTerminal!({ hostId: "node:node-1", threadId: "ses_remote" }),
).resolves.toEqual({
@@ -510,7 +468,7 @@ describe("OpenCode session catalog", () => {
registerOpenCodeSessionCatalog(api);
const catalog = provider;
expect(catalog).toBeDefined();
await catalog!.list({ hostIds: ["node:node-1"], search: " " });
await catalog!.list({ hostIds: ["node:node-1"] });
await catalog!.read({ hostId: "node:node-1", threadId: "ses_remote" });
expect(invoke).toHaveBeenNthCalledWith(1, {