diff --git a/src/acp/translator.lifecycle.test.ts b/src/acp/translator.lifecycle.test.ts index 3b7c847cfa7..5736f916330 100644 --- a/src/acp/translator.lifecycle.test.ts +++ b/src/acp/translator.lifecycle.test.ts @@ -254,6 +254,33 @@ describe("acp translator stable lifecycle handlers", () => { sessionStore.clearAllSessionsForTest(); }); + it("lists Gateway sessions with invalid updated timestamps", async () => { + const request = vi.fn(async (method: string) => { + if (method === "sessions.list") { + return createGatewaySessions([ + createSessionRow({ + key: "agent:main:work", + cwd: "/tmp/openclaw", + title: "Work session", + updatedAt: Number.POSITIVE_INFINITY, + }), + ]); + } + return { ok: true }; + }) as GatewayClient["request"]; + const sessionStore = createInMemorySessionStore(); + const agent = new AcpGatewayAgent(createAcpConnection(), createAcpGateway(request), { + sessionStore, + }); + + const result = await agent.listSessions(createListSessionsRequest({ cwd: "/tmp/openclaw" })); + + expect(result.sessions).toHaveLength(1); + expect(result.sessions[0]?.updatedAt).toBeUndefined(); + + sessionStore.clearAllSessionsForTest(); + }); + it("rejects session/list cursors when the cwd filter changes", async () => { const allRows = [ createSessionRow({ key: "agent:main:a1", cwd: "/work/a", title: "A1" }), diff --git a/src/acp/translator.ts b/src/acp/translator.ts index 5deda746831..230894663f6 100644 --- a/src/acp/translator.ts +++ b/src/acp/translator.ts @@ -42,6 +42,7 @@ import { resolveFixedWindowRateLimitInteger, type FixedWindowRateLimiter, } from "../infra/fixed-window-rate-limit.js"; +import { timestampMsToIsoString } from "../shared/number-coercion.js"; import { normalizeOptionalString } from "../shared/string-coerce.js"; import { shortenHomePath } from "../utils.js"; import { @@ -502,10 +503,7 @@ function buildSessionMetadata(params: { normalizeOptionalString(params.row?.displayName) || normalizeOptionalString(params.row?.label) || params.sessionKey; - const updatedAt = - typeof params.row?.updatedAt === "number" && Number.isFinite(params.row.updatedAt) - ? new Date(params.row.updatedAt).toISOString() - : null; + const updatedAt = timestampMsToIsoString(params.row?.updatedAt) ?? null; return { title, updatedAt, @@ -1914,7 +1912,7 @@ export class AcpGatewayAgent implements Agent { sessionId: session.key, cwd, title: session.derivedTitle ?? session.displayName ?? session.label ?? session.key, - updatedAt: session.updatedAt ? new Date(session.updatedAt).toISOString() : undefined, + updatedAt: timestampMsToIsoString(session.updatedAt), _meta: toAcpSessionLineageMeta(session), }; }