mirror of
https://github.com/NoeFabris/opencode-antigravity-auth.git
synced 2026-05-13 23:53:18 +00:00
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:
@@ -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-")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user