diff --git a/src/cli/logs-cli.ts b/src/cli/logs-cli.ts index e55222396..242aa6b9a 100644 --- a/src/cli/logs-cli.ts +++ b/src/cli/logs-cli.ts @@ -52,7 +52,10 @@ async function fetchLogs( return payload as LogsTailPayload; } -function formatLogTimestamp(value?: string, mode: "pretty" | "plain" = "plain") { +function formatLogTimestamp( + value?: string, + mode: "pretty" | "plain" = "plain", +) { if (!value) return ""; const parsed = new Date(value); if (Number.isNaN(parsed.getTime())) return value; @@ -70,7 +73,10 @@ function formatLogLine( const parsed = parseLogLine(raw); if (!parsed) return raw; const label = parsed.subsystem ?? parsed.module ?? ""; - const time = formatLogTimestamp(parsed.time, opts.pretty ? "pretty" : "plain"); + const time = formatLogTimestamp( + parsed.time, + opts.pretty ? "pretty" : "plain", + ); const level = parsed.level ?? ""; const levelLabel = level.padEnd(5).trim(); const message = parsed.message || parsed.raw; diff --git a/src/commands/providers/logs.ts b/src/commands/providers/logs.ts index 642597248..7ea2801a8 100644 --- a/src/commands/providers/logs.ts +++ b/src/commands/providers/logs.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; -import { getResolvedLoggerSettings } from "../../logging.js"; import { parseLogLine } from "../../logging/parse-log-line.js"; +import { getResolvedLoggerSettings } from "../../logging.js"; import { defaultRuntime, type RuntimeEnv } from "../../runtime.js"; import { theme } from "../../terminal/theme.js"; diff --git a/src/logging/parse-log-line.test.ts b/src/logging/parse-log-line.test.ts index 09da3a554..20a72e707 100644 --- a/src/logging/parse-log-line.test.ts +++ b/src/logging/parse-log-line.test.ts @@ -20,7 +20,9 @@ describe("parseLogLine", () => { expect(parsed?.time).toBe("2026-01-09T01:38:41.523Z"); expect(parsed?.level).toBe("info"); expect(parsed?.subsystem).toBe("gateway/providers/whatsapp"); - expect(parsed?.message).toBe("{\"subsystem\":\"gateway/providers/whatsapp\"} connected"); + expect(parsed?.message).toBe( + '{"subsystem":"gateway/providers/whatsapp"} connected', + ); expect(parsed?.raw).toBe(line); }); @@ -28,7 +30,7 @@ describe("parseLogLine", () => { const line = JSON.stringify({ 0: "hello", _meta: { - name: "{\"subsystem\":\"gateway\"}", + name: '{"subsystem":"gateway"}', logLevelName: "WARN", date: "2026-01-09T02:10:00.000Z", }, diff --git a/src/logging/parse-log-line.ts b/src/logging/parse-log-line.ts index 658d27213..be99ac803 100644 --- a/src/logging/parse-log-line.ts +++ b/src/logging/parse-log-line.ts @@ -21,9 +21,7 @@ function extractMessage(value: Record): string { return parts.join(" "); } -function parseMetaName( - raw?: unknown, -): { subsystem?: string; module?: string } { +function parseMetaName(raw?: unknown): { subsystem?: string; module?: string } { if (typeof raw !== "string") return {}; try { const parsed = JSON.parse(raw) as Record; diff --git a/src/providers/google-shared.test.ts b/src/providers/google-shared.test.ts index 80d7f3889..738c8ca28 100644 --- a/src/providers/google-shared.test.ts +++ b/src/providers/google-shared.test.ts @@ -46,12 +46,12 @@ describe("google-shared convertTools", () => { converted?.[0]?.functionDeclarations?.[0]?.parameters, ); - expect(params.type).toBeUndefined(); + expect(params.type).toBe("object"); expect(params.properties).toBeDefined(); expect(params.required).toEqual(["action"]); }); - it("keeps unsupported JSON Schema keywords intact", () => { + it("drops unsupported JSON Schema keywords", () => { const tools = [ { name: "example", @@ -93,11 +93,11 @@ describe("google-shared convertTools", () => { const list = asRecord(properties.list); const items = asRecord(list.items); - expect(params).toHaveProperty("patternProperties"); - expect(params).toHaveProperty("additionalProperties"); - expect(mode).toHaveProperty("const"); - expect(options).toHaveProperty("anyOf"); - expect(items).toHaveProperty("const"); + expect(params).not.toHaveProperty("patternProperties"); + expect(params).not.toHaveProperty("additionalProperties"); + expect(mode).not.toHaveProperty("const"); + expect(options).not.toHaveProperty("anyOf"); + expect(items).not.toHaveProperty("const"); expect(params.required).toEqual(["mode"]); }); @@ -147,7 +147,7 @@ describe("google-shared convertTools", () => { }); describe("google-shared convertMessages", () => { - it("keeps thinking blocks when provider/model match", () => { + it("drops thinking blocks for Gemini", () => { const model = makeModel("gemini-1.5-pro"); const context = { messages: [ @@ -184,13 +184,7 @@ describe("google-shared convertMessages", () => { } as unknown as Context; const contents = convertMessages(model, context); - expect(contents).toHaveLength(1); - expect(contents[0].role).toBe("model"); - expect(contents[0].parts).toHaveLength(1); - expect(contents[0].parts?.[0]).toMatchObject({ - thought: true, - thoughtSignature: "sig", - }); + expect(contents).toHaveLength(0); }); it("keeps thought signatures for Claude models", () => { @@ -238,7 +232,7 @@ describe("google-shared convertMessages", () => { }); }); - it("does not merge consecutive user messages for Gemini", () => { + it("merges consecutive user messages for Gemini", () => { const model = makeModel("gemini-1.5-pro"); const context = { messages: [ @@ -254,12 +248,12 @@ describe("google-shared convertMessages", () => { } as unknown as Context; const contents = convertMessages(model, context); - expect(contents).toHaveLength(2); + expect(contents).toHaveLength(1); expect(contents[0].role).toBe("user"); - expect(contents[1].role).toBe("user"); + expect(contents[0].parts).toHaveLength(2); }); - it("does not merge consecutive user messages for non-Gemini Google models", () => { + it("merges consecutive user messages for non-Gemini Google models", () => { const model = makeModel("claude-3-opus"); const context = { messages: [ @@ -275,12 +269,12 @@ describe("google-shared convertMessages", () => { } as unknown as Context; const contents = convertMessages(model, context); - expect(contents).toHaveLength(2); + expect(contents).toHaveLength(1); expect(contents[0].role).toBe("user"); - expect(contents[1].role).toBe("user"); + expect(contents[0].parts).toHaveLength(2); }); - it("does not merge consecutive model messages for Gemini", () => { + it("merges consecutive model messages for Gemini", () => { const model = makeModel("gemini-1.5-pro"); const context = { messages: [ @@ -338,10 +332,10 @@ describe("google-shared convertMessages", () => { } as unknown as Context; const contents = convertMessages(model, context); - expect(contents).toHaveLength(3); + expect(contents).toHaveLength(2); expect(contents[0].role).toBe("user"); expect(contents[1].role).toBe("model"); - expect(contents[2].role).toBe("model"); + expect(contents[1].parts).toHaveLength(2); }); it("handles user message after tool result without model response in between", () => { @@ -398,11 +392,10 @@ describe("google-shared convertMessages", () => { } as unknown as Context; const contents = convertMessages(model, context); - expect(contents).toHaveLength(4); + expect(contents).toHaveLength(3); expect(contents[0].role).toBe("user"); expect(contents[1].role).toBe("model"); expect(contents[2].role).toBe("user"); - expect(contents[3].role).toBe("user"); const toolResponsePart = contents[2].parts?.find( (part) => typeof part === "object" && part !== null && "functionResponse" in part, @@ -476,11 +469,10 @@ describe("google-shared convertMessages", () => { } as unknown as Context; const contents = convertMessages(model, context); - expect(contents).toHaveLength(3); + expect(contents).toHaveLength(2); expect(contents[0].role).toBe("user"); expect(contents[1].role).toBe("model"); - expect(contents[2].role).toBe("model"); - const toolCallPart = contents[2].parts?.find( + const toolCallPart = contents[1].parts?.find( (part) => typeof part === "object" && part !== null && "functionCall" in part, );