hive(03-decouple-debug-gates-and-update-tests-to-target-behavior): Updated src/plugin/debug.ts to decouple sink sta

This commit is contained in:
Noe
2026-02-20 17:09:52 +00:00
parent 5770288833
commit 79516e1ab5
6 changed files with 236 additions and 231 deletions

View File

@@ -1,69 +1,78 @@
import { mkdtempSync } from "node:fs"
import { tmpdir } from "node:os"
import { join } from "node:path"
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
import { DEFAULT_CONFIG } from "./config"
vi.mock("./storage", () => ({
ensureGitignoreSync: vi.fn(),
const { ensureGitignoreSyncMock } = vi.hoisted(() => ({
ensureGitignoreSyncMock: vi.fn(),
}))
function createTmpDir(prefix: string): string {
return mkdtempSync(join(tmpdir(), prefix))
}
vi.mock("./storage", () => ({
ensureGitignoreSync: ensureGitignoreSyncMock,
}))
beforeEach(() => {
vi.resetModules()
vi.stubEnv("OPENCODE_ANTIGRAVITY_DEBUG", "")
vi.stubEnv("OPENCODE_ANTIGRAVITY_DEBUG_TUI", "")
})
describe("debug sink policy", () => {
let originalDebugEnv: string | undefined
let originalDebugTuiEnv: string | undefined
afterEach(() => {
vi.unstubAllEnvs()
})
beforeEach(() => {
vi.resetModules()
originalDebugEnv = process.env.OPENCODE_ANTIGRAVITY_DEBUG
originalDebugTuiEnv = process.env.OPENCODE_ANTIGRAVITY_DEBUG_TUI
delete process.env.OPENCODE_ANTIGRAVITY_DEBUG
delete process.env.OPENCODE_ANTIGRAVITY_DEBUG_TUI
ensureGitignoreSyncMock.mockReset()
})
describe("debug.ts baseline coupling", () => {
it("keeps TUI debug disabled when debug=false and debug_tui=true", async () => {
const configDir = createTmpDir("antigravity-debug-config-")
const logDir = createTmpDir("antigravity-debug-logs-")
vi.stubEnv("XDG_CONFIG_HOME", configDir)
afterEach(() => {
if (originalDebugEnv === undefined) {
delete process.env.OPENCODE_ANTIGRAVITY_DEBUG
} else {
process.env.OPENCODE_ANTIGRAVITY_DEBUG = originalDebugEnv
}
const { DEFAULT_CONFIG } = await import("./config")
if (originalDebugTuiEnv === undefined) {
delete process.env.OPENCODE_ANTIGRAVITY_DEBUG_TUI
} else {
process.env.OPENCODE_ANTIGRAVITY_DEBUG_TUI = originalDebugTuiEnv
}
})
it("keeps debug_tui independent from debug in config", async () => {
const { initializeDebug, isDebugEnabled, isDebugTuiEnabled, getLogFilePath } = await import("./debug")
initializeDebug({ ...DEFAULT_CONFIG, debug: false, debug_tui: true, log_dir: logDir })
initializeDebug({
...DEFAULT_CONFIG,
debug: false,
debug_tui: true,
})
expect(isDebugEnabled()).toBe(false)
expect(isDebugTuiEnabled()).toBe(false)
expect(isDebugTuiEnabled()).toBe(true)
expect(getLogFilePath()).toBeUndefined()
})
it("enables file debug only when debug=true and debug_tui=false", async () => {
const configDir = createTmpDir("antigravity-debug-config-")
const logDir = createTmpDir("antigravity-debug-logs-")
vi.stubEnv("XDG_CONFIG_HOME", configDir)
it("keeps debug_tui independent from debug in env fallback", async () => {
process.env.OPENCODE_ANTIGRAVITY_DEBUG = "0"
process.env.OPENCODE_ANTIGRAVITY_DEBUG_TUI = "1"
const { DEFAULT_CONFIG } = await import("./config")
const { isDebugEnabled, isDebugTuiEnabled, getLogFilePath } = await import("./debug")
expect(isDebugEnabled()).toBe(false)
expect(isDebugTuiEnabled()).toBe(true)
expect(getLogFilePath()).toBeUndefined()
})
it("keeps file debug enabled without TUI when only debug is true", async () => {
const { initializeDebug, isDebugEnabled, isDebugTuiEnabled, getLogFilePath } = await import("./debug")
initializeDebug({ ...DEFAULT_CONFIG, debug: true, debug_tui: false, log_dir: logDir })
initializeDebug({
...DEFAULT_CONFIG,
debug: true,
debug_tui: false,
log_dir: "/tmp/opencode-antigravity-debug-tests",
})
expect(isDebugEnabled()).toBe(true)
expect(isDebugTuiEnabled()).toBe(false)
expect(getLogFilePath()).toBeTruthy()
})
it("enables both file and TUI debug when debug=true and debug_tui=true", async () => {
const configDir = createTmpDir("antigravity-debug-config-")
const logDir = createTmpDir("antigravity-debug-logs-")
vi.stubEnv("XDG_CONFIG_HOME", configDir)
const { DEFAULT_CONFIG } = await import("./config")
const { initializeDebug, isDebugEnabled, isDebugTuiEnabled, getLogFilePath } = await import("./debug")
initializeDebug({ ...DEFAULT_CONFIG, debug: true, debug_tui: true, log_dir: logDir })
expect(isDebugEnabled()).toBe(true)
expect(isDebugTuiEnabled()).toBe(true)
expect(getLogFilePath()).toBeTruthy()
expect(getLogFilePath()).toContain("antigravity-debug-")
})
})

View File

@@ -3,14 +3,6 @@ import { join } from "node:path";
import { homedir } from "node:os";
import { env } from "node:process";
import type { AntigravityConfig } from "./config";
import {
deriveDebugPolicy,
formatAccountContextLabel,
formatAccountLabel,
formatBodyPreviewForLog,
formatErrorForLog,
truncateTextForLog,
} from "./logging-utils";
import { ensureGitignoreSync } from "./storage";
const MAX_BODY_PREVIEW_CHARS = 12000;
@@ -33,6 +25,17 @@ interface DebugState {
let debugState: DebugState | null = null;
/**
* Parse debug level from a flag string.
* 0 = off, 1 = basic, 2 = verbose (full bodies)
*/
function parseDebugLevel(flag: string): number {
const trimmed = flag.trim();
if (trimmed === "2" || trimmed === "verbose") return 2;
if (trimmed === "1" || trimmed === "true") return 1;
return 0;
}
/**
* Get the OS-specific config directory.
*/
@@ -130,12 +133,10 @@ function createLogWriter(filePath?: string): (line: string) => void {
export function initializeDebug(config: AntigravityConfig): void {
// Config takes precedence, but env var can force enable for debugging
const envDebugFlag = env.OPENCODE_ANTIGRAVITY_DEBUG ?? "";
const { debugLevel, debugEnabled, verboseEnabled, debugTuiEnabled } = deriveDebugPolicy({
configDebug: config.debug,
configDebugTui: config.debug_tui,
envDebugFlag,
envDebugTuiFlag: env.OPENCODE_ANTIGRAVITY_DEBUG_TUI,
});
const debugLevel = config.debug ? (envDebugFlag === "2" || envDebugFlag === "verbose" ? 2 : 1) : parseDebugLevel(envDebugFlag);
const debugEnabled = debugLevel >= 1;
const verboseEnabled = debugLevel >= 2;
const debugTuiEnabled = config.debug_tui || env.OPENCODE_ANTIGRAVITY_DEBUG_TUI === "1" || env.OPENCODE_ANTIGRAVITY_DEBUG_TUI === "true";
const logFilePath = debugEnabled ? createLogFilePath(config.log_dir) : undefined;
const logWriter = createLogWriter(logFilePath);
@@ -160,12 +161,11 @@ export function initializeDebug(config: AntigravityConfig): void {
function getDebugState(): DebugState {
if (!debugState) {
// Fallback to env-based initialization for backward compatibility
const { debugLevel, debugEnabled, verboseEnabled, debugTuiEnabled } = deriveDebugPolicy({
configDebug: false,
configDebugTui: false,
envDebugFlag: env.OPENCODE_ANTIGRAVITY_DEBUG,
envDebugTuiFlag: env.OPENCODE_ANTIGRAVITY_DEBUG_TUI,
});
const envDebugFlag = env.OPENCODE_ANTIGRAVITY_DEBUG ?? "";
const debugLevel = parseDebugLevel(envDebugFlag);
const debugEnabled = debugLevel >= 1;
const verboseEnabled = debugLevel >= 2;
const debugTuiEnabled = env.OPENCODE_ANTIGRAVITY_DEBUG_TUI === "1" || env.OPENCODE_ANTIGRAVITY_DEBUG_TUI === "true";
const logFilePath = debugEnabled ? createLogFilePath() : undefined;
const logWriter = createLogWriter(logFilePath);
@@ -246,7 +246,7 @@ export function startAntigravityDebugRequest(meta: AntigravityDebugRequestMeta):
}
logDebug(`[Antigravity Debug ${id}] Streaming: ${meta.streaming ? "yes" : "no"}`);
logDebug(`[Antigravity Debug ${id}] Headers: ${JSON.stringify(maskHeaders(meta.headers))}`);
const bodyPreview = formatBodyPreviewForLog(meta.body, MAX_BODY_PREVIEW_CHARS);
const bodyPreview = formatBodyPreview(meta.body);
if (bodyPreview) {
logDebug(`[Antigravity Debug ${id}] Body Preview: ${bodyPreview}`);
}
@@ -282,12 +282,12 @@ export function logAntigravityDebugResponse(
}
if (meta.error) {
logDebug(`[Antigravity Debug ${context.id}] Error: ${formatErrorForLog(meta.error)}`);
logDebug(`[Antigravity Debug ${context.id}] Error: ${formatError(meta.error)}`);
}
if (meta.body) {
logDebug(
`[Antigravity Debug ${context.id}] Response Body Preview: ${truncateTextForLog(meta.body, MAX_BODY_PREVIEW_CHARS)}`,
`[Antigravity Debug ${context.id}] Response Body Preview: ${truncateForLog(meta.body)}`,
);
}
}
@@ -312,6 +312,43 @@ function maskHeaders(headers?: HeadersInit | Headers): Record<string, string> {
return result;
}
/**
* Produces a short, type-aware preview of a request/response body for logs.
*/
function formatBodyPreview(body?: BodyInit | null): string | undefined {
if (body == null) {
return undefined;
}
if (typeof body === "string") {
return truncateForLog(body);
}
if (body instanceof URLSearchParams) {
return truncateForLog(body.toString());
}
if (typeof Blob !== "undefined" && body instanceof Blob) {
return `[Blob size=${body.size}]`;
}
if (typeof FormData !== "undefined" && body instanceof FormData) {
return "[FormData payload omitted]";
}
return `[${body.constructor?.name ?? typeof body} payload omitted]`;
}
/**
* Truncates long strings to a fixed preview length for logging.
*/
function truncateForLog(text: string): string {
if (text.length <= MAX_BODY_PREVIEW_CHARS) {
return text;
}
return `${text.slice(0, MAX_BODY_PREVIEW_CHARS)}... (truncated ${text.length - MAX_BODY_PREVIEW_CHARS} chars)`;
}
/**
* Writes a single debug line using the configured writer.
*/
@@ -319,6 +356,20 @@ function logDebug(line: string): void {
getDebugState().logWriter(line);
}
/**
* Converts unknown error-like values into printable strings.
*/
function formatError(error: unknown): string {
if (error instanceof Error) {
return error.stack ?? error.message;
}
try {
return JSON.stringify(error);
} catch {
return String(error);
}
}
export interface AccountDebugInfo {
index: number;
email?: string;
@@ -330,7 +381,11 @@ export interface AccountDebugInfo {
export function logAccountContext(label: string, info: AccountDebugInfo): void {
if (!getDebugState().debugEnabled) return;
const accountLabel = formatAccountContextLabel(info.email, info.index);
const accountLabel = info.email
? info.email
: info.index >= 0
? `Account ${info.index + 1}`
: "All accounts";
const indexLabel = info.index >= 0 ? `${info.index + 1}/${info.totalAccounts}` : `-/${info.totalAccounts}`;
@@ -361,7 +416,7 @@ export function logRateLimitEvent(
bodyInfo: { message?: string; quotaResetTime?: string; retryDelayMs?: number | null; reason?: string },
): void {
if (!getDebugState().debugEnabled) return;
const accountLabel = formatAccountLabel(email, accountIndex);
const accountLabel = email || `Account ${accountIndex + 1}`;
logDebug(`[RateLimit] ${status} on ${accountLabel} family=${family} retryAfterMs=${retryAfterMs}`);
if (bodyInfo.message) {
logDebug(`[RateLimit] message: ${bodyInfo.message}`);
@@ -384,7 +439,7 @@ export function logRateLimitSnapshot(
if (!getDebugState().debugEnabled) return;
const now = Date.now();
const entries = accounts.map((account) => {
const label = formatAccountLabel(account.email, account.index);
const label = account.email ? account.email : `Account ${account.index + 1}`;
const reset = account.rateLimitResetTimes?.[family as "claude" | "gemini"];
if (typeof reset !== "number") {
return `${label}=ready`;
@@ -412,11 +467,13 @@ export async function logResponseBody(
try {
const text = await response.clone().text();
const maxChars = state.verboseEnabled ? MAX_BODY_VERBOSE_CHARS : MAX_BODY_PREVIEW_CHARS;
const preview = truncateTextForLog(text, maxChars);
const preview = text.length <= maxChars
? text
: `${text.slice(0, maxChars)}... (truncated ${text.length - maxChars} chars)`;
logDebug(`[Antigravity Debug ${context.id}] Response Body (${status}): ${preview}`);
return text;
} catch (e) {
logDebug(`[Antigravity Debug ${context.id}] Failed to read response body: ${formatErrorForLog(e)}`);
logDebug(`[Antigravity Debug ${context.id}] Failed to read response body: ${formatError(e)}`);
return undefined;
}
}
@@ -484,7 +541,7 @@ export function logQuotaStatus(
family?: string,
): void {
if (!getDebugState().debugEnabled) return;
const accountLabel = formatAccountLabel(accountEmail, accountIndex);
const accountLabel = accountEmail || `Account ${accountIndex + 1}`;
const familyInfo = family ? ` family=${family}` : "";
const status = quotaPercent <= 0 ? "EXHAUSTED" : quotaPercent < 20 ? "LOW" : "OK";
logDebug(`[Quota] ${accountLabel} remaining=${quotaPercent.toFixed(1)}% status=${status}${familyInfo}`);

View File

@@ -1,74 +1,80 @@
import { mkdtempSync } from "node:fs"
import { tmpdir } from "node:os"
import { join } from "node:path"
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
import { DEFAULT_CONFIG } from "./config"
import type { PluginClient } from "./types"
vi.mock("./storage", () => ({
ensureGitignoreSync: vi.fn(),
const { ensureGitignoreSyncMock } = vi.hoisted(() => ({
ensureGitignoreSyncMock: vi.fn(),
}))
function createTmpDir(prefix: string): string {
return mkdtempSync(join(tmpdir(), prefix))
}
vi.mock("./storage", () => ({
ensureGitignoreSync: ensureGitignoreSyncMock,
}))
beforeEach(() => {
vi.resetModules()
vi.stubEnv("OPENCODE_ANTIGRAVITY_DEBUG", "")
vi.stubEnv("OPENCODE_ANTIGRAVITY_DEBUG_TUI", "")
vi.stubEnv("OPENCODE_ANTIGRAVITY_CONSOLE_LOG", "")
})
describe("logger sink routing", () => {
beforeEach(() => {
vi.resetModules()
ensureGitignoreSyncMock.mockReset()
})
afterEach(() => {
vi.unstubAllEnvs()
})
describe("logger.ts baseline sink routing", () => {
async function runLoggerWithFlags(debug: boolean, debugTui: boolean): Promise<{ appLog: ReturnType<typeof vi.fn> }> {
const configDir = createTmpDir("antigravity-logger-config-")
const logDir = createTmpDir("antigravity-logger-logs-")
vi.stubEnv("XDG_CONFIG_HOME", configDir)
const { DEFAULT_CONFIG } = await import("./config")
afterEach(async () => {
const { initializeDebug } = await import("./debug")
const { initLogger, createLogger } = await import("./logger")
initializeDebug(DEFAULT_CONFIG)
})
initializeDebug({ ...DEFAULT_CONFIG, debug, debug_tui: debugTui, log_dir: logDir })
it("routes logs to TUI when debug_tui is enabled without file debug", async () => {
const { initializeDebug } = await import("./debug")
const { createLogger, initLogger } = await import("./logger")
initializeDebug({
...DEFAULT_CONFIG,
debug: false,
debug_tui: true,
})
const appLog = vi.fn().mockResolvedValue(undefined)
const client = {
app: {
log: appLog,
},
}
} as unknown as PluginClient
initLogger(client as unknown as PluginClient)
const logger = createLogger("baseline")
logger.debug("testing baseline routing", { debug, debugTui })
initLogger(client)
return { appLog }
}
createLogger("request").debug("thinking-resolution", { status: 429 })
it("does not emit TUI logs when debug=false and debug_tui=true", async () => {
const { appLog } = await runLoggerWithFlags(false, true)
expect(appLog).not.toHaveBeenCalled()
})
it("does not emit TUI logs when debug=true and debug_tui=false", async () => {
const { appLog } = await runLoggerWithFlags(true, false)
expect(appLog).not.toHaveBeenCalled()
})
it("emits TUI logs when debug=true and debug_tui=true", async () => {
const { appLog } = await runLoggerWithFlags(true, true)
expect(appLog).toHaveBeenCalledTimes(1)
expect(appLog).toHaveBeenCalledWith({
body: {
service: "antigravity.baseline",
service: "antigravity.request",
level: "debug",
message: "testing baseline routing",
extra: { debug: true, debugTui: true },
message: "thinking-resolution",
extra: { status: 429 },
},
})
})
it("does not route to TUI when only file debug is enabled", async () => {
const { initializeDebug } = await import("./debug")
const { createLogger, initLogger } = await import("./logger")
initializeDebug({
...DEFAULT_CONFIG,
debug: true,
debug_tui: false,
log_dir: "/tmp/opencode-antigravity-logger-tests",
})
const appLog = vi.fn().mockResolvedValue(undefined)
const client = {
app: {
log: appLog,
},
} as unknown as PluginClient
initLogger(client)
createLogger("request").debug("file-only")
expect(appLog).not.toHaveBeenCalled()
})
})

View File

@@ -2,9 +2,9 @@
* Structured Logger for Antigravity Plugin
*
* Logging behavior:
* - debug disabled → no logs anywhere
* - debug enabled → log files only (via debug.ts logWriter)
* - debug_tui enabled → log files + TUI log panel
* - debug controls file logs only (via debug.ts)
* - debug_tui controls TUI log panel only
* - either sink can be enabled independently
* - OPENCODE_ANTIGRAVITY_CONSOLE_LOG=1 → console output (independent of debug flags)
*/
@@ -68,7 +68,7 @@ export function createLogger(module: string): Logger {
const service = `antigravity.${module}`;
const log = (level: LogLevel, message: string, extra?: Record<string, unknown>): void => {
// TUI logging: only when debug TUI is enabled
// TUI logging: controlled only by debug_tui policy
if (isDebugTuiEnabled()) {
const app = _client?.app;
if (app && typeof app.log === "function") {

View File

@@ -1,7 +1,4 @@
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, it, expect, vi } from "vitest";
import { describe, it, expect } from "vitest";
import {
prepareAntigravityRequest,
transformAntigravityResponse,
@@ -9,8 +6,8 @@ import {
isGenerativeLanguageRequest,
__testExports,
} from "./request";
import { DEFAULT_CONFIG, initRuntimeConfig } from "./config";
import { DEBUG_MESSAGE_PREFIX, initializeDebug } from "./debug";
import { DEFAULT_CONFIG } from "./config";
import { initializeDebug } from "./debug";
import type { SignatureStore, ThoughtBuffer, StreamingCallbacks, StreamingOptions } from "./core/streaming/types";
const {
@@ -58,14 +55,6 @@ const defaultCallbacks: StreamingCallbacks = {};
const defaultOptions: StreamingOptions = {};
const defaultDebugState = { injected: false };
function createTestDir(prefix: string): string {
return mkdtempSync(join(tmpdir(), prefix));
}
afterEach(() => {
vi.unstubAllEnvs();
});
describe("request.ts", () => {
describe("getPluginSessionId", () => {
it("returns consistent session ID across calls", () => {
@@ -924,110 +913,53 @@ it("removes x-api-key header", () => {
});
describe("transformAntigravityResponse", () => {
const createSuccessResponse = () =>
new Response(
it("injects [ThinkingResolution] details when debug_tui is enabled", async () => {
initializeDebug({
...DEFAULT_CONFIG,
debug: false,
debug_tui: true,
});
const response = new Response(
JSON.stringify({
response: {
candidates: [
{
content: {
parts: [{ text: "hello" }],
},
},
],
error: {
code: 500,
message: "Upstream error",
status: "INTERNAL",
},
}),
{
status: 200,
status: 500,
headers: { "content-type": "application/json" },
},
);
it("does not inject debug thinking when debug=false and debug_tui=true", async () => {
const configDir = createTestDir("antigravity-request-config-");
const logDir = createTestDir("antigravity-request-logs-");
vi.stubEnv("XDG_CONFIG_HOME", configDir);
initializeDebug({ ...DEFAULT_CONFIG, debug: false, debug_tui: true, log_dir: logDir });
initRuntimeConfig({ ...DEFAULT_CONFIG, keep_thinking: false, log_dir: logDir });
const transformed = await transformAntigravityResponse(
createSuccessResponse(),
response,
false,
undefined,
"gemini-2.5-pro",
"test-project",
"https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:generateContent",
"gemini-2.5-pro",
"session-1",
0,
"summary",
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
["ThinkingResolution: debug line"],
[
"status=500 INTERNAL",
"endpoint=https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:generateContent",
"account=test@example.com",
],
);
const body = JSON.parse(await transformed.text());
expect(body.candidates[0].content.parts).toEqual([{ text: "hello" }]);
expect(body.candidates[0].reasoning_content).toBeUndefined();
});
const bodyText = await transformed.text();
expect(bodyText).toContain("[ThinkingResolution]");
expect(bodyText).toContain("status=500 INTERNAL");
expect(bodyText).toContain("endpoint=https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:generateContent");
expect(bodyText).toContain("account=test@example.com");
it("does not inject debug thinking when debug=true and debug_tui=false", async () => {
const configDir = createTestDir("antigravity-request-config-");
const logDir = createTestDir("antigravity-request-logs-");
vi.stubEnv("XDG_CONFIG_HOME", configDir);
initializeDebug({ ...DEFAULT_CONFIG, debug: true, debug_tui: false, log_dir: logDir });
initRuntimeConfig({ ...DEFAULT_CONFIG, keep_thinking: false, log_dir: logDir });
const transformed = await transformAntigravityResponse(
createSuccessResponse(),
false,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
["ThinkingResolution: debug line"],
);
const body = JSON.parse(await transformed.text());
expect(body.candidates[0].content.parts).toEqual([{ text: "hello" }]);
expect(body.candidates[0].reasoning_content).toBeUndefined();
});
it("injects debug thinking only when debug=true and debug_tui=true", async () => {
const configDir = createTestDir("antigravity-request-config-");
const logDir = createTestDir("antigravity-request-logs-");
vi.stubEnv("XDG_CONFIG_HOME", configDir);
initializeDebug({ ...DEFAULT_CONFIG, debug: true, debug_tui: true, log_dir: logDir });
initRuntimeConfig({ ...DEFAULT_CONFIG, keep_thinking: false, log_dir: logDir });
const transformed = await transformAntigravityResponse(
createSuccessResponse(),
false,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
["ThinkingResolution: debug line"],
);
const body = JSON.parse(await transformed.text());
expect(body.candidates[0].content.parts).toHaveLength(2);
expect(body.candidates[0].content.parts[0].type).toBe("reasoning");
expect(body.candidates[0].content.parts[0].text).toContain(DEBUG_MESSAGE_PREFIX);
expect(body.candidates[0].content.parts[0].text).toContain("ThinkingResolution: debug line");
expect(body.candidates[0].reasoning_content).toContain(DEBUG_MESSAGE_PREFIX);
initializeDebug(DEFAULT_CONFIG);
});
it("does not misclassify generic INVALID_ARGUMENT as thinking recovery from debug metadata", async () => {

View File

@@ -227,7 +227,8 @@ function formatDebugLinesForThinking(lines: string[]): string {
.map((line) => line.trim())
.filter((line) => line.length > 0)
.slice(-50);
return `${DEBUG_MESSAGE_PREFIX}\n${cleaned.map((line) => `- ${line}`).join("\n")}`;
const prelude = `[ThinkingResolution] source=debug_tui lines=${cleaned.length}`;
return `${DEBUG_MESSAGE_PREFIX}\n- ${prelude}\n${cleaned.map((line) => `- ${line}`).join("\n")}`;
}
function injectDebugThinking(response: unknown, debugText: string): unknown {