Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 2 additions & 28 deletions src/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Plugin, PluginInput } from "@opencode-ai/plugin";
import { tool } from "@opencode-ai/plugin";
import type { Auth } from "@opencode-ai/sdk";
import { appendFileSync, existsSync, mkdirSync, realpathSync } from "fs";
import { realpathSync } from "fs";
import { mkdir } from "fs/promises";
import { homedir } from "os";
import { isAbsolute, join, relative, resolve } from "path";
Expand Down Expand Up @@ -62,31 +62,6 @@ import { formatShellCommandForPlatform, resolveCursorAgentBinary } from "./utils

const log = createLogger("plugin");

// Debug log file for tool-loop investigation
const DEBUG_LOG_DIR = join(homedir(), ".config", "opencode", "logs");
const DEBUG_LOG_FILE = join(DEBUG_LOG_DIR, "tool-loop-debug.log");

function ensureDebugLogDir(): void {
try {
if (!existsSync(DEBUG_LOG_DIR)) {
mkdir(DEBUG_LOG_DIR, { recursive: true }).catch(() => {});
}
} catch {
// Ignore errors creating log directory
}
}

function debugLogToFile(message: string, data: any): void {
try {
ensureDebugLogDir();
const timestamp = new Date().toISOString();
const logLine = `[${timestamp}] ${message}: ${JSON.stringify(data, null, 2)}\n`;
appendFileSync(DEBUG_LOG_FILE, logLine);
} catch {
// Ignore errors
}
}

interface McpToolSummary {
serverName: string;
toolName: string;
Expand Down Expand Up @@ -707,8 +682,7 @@ async function ensureCursorProxyServer(workspaceDirectory: string, toolRouter?:
const stream = body?.stream === true;
const tools = Array.isArray(body?.tools) ? body.tools : [];

// DEBUG: Log raw request structure for tool-loop investigation
debugLogToFile("raw_request_body", {
log.debug("raw request body", {
model: body?.model,
cursorModel: body?.cursorModel,
stream,
Expand Down
39 changes: 4 additions & 35 deletions src/proxy/prompt-builder.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,13 @@
import { appendFileSync, existsSync, mkdirSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
import { createLogger } from "../utils/logger.js";

const log = createLogger("proxy:prompt-builder");

// Debug log file for tool-loop investigation
const DEBUG_LOG_DIR = join(homedir(), ".config", "opencode", "logs");
const DEBUG_LOG_FILE = join(DEBUG_LOG_DIR, "tool-loop-debug.log");

function ensureLogDir(): void {
try {
if (!existsSync(DEBUG_LOG_DIR)) {
mkdirSync(DEBUG_LOG_DIR, { recursive: true });
}
} catch {
// Ignore errors creating log directory
}
}

function debugLogToFile(message: string, data: any): void {
try {
ensureLogDir();
const timestamp = new Date().toISOString();
const logLine = `[${timestamp}] ${message}: ${JSON.stringify(data, null, 2)}\n`;
appendFileSync(DEBUG_LOG_FILE, logLine);
} catch (err) {
// Fall back to regular debug log if file writing fails
log.debug(message, data);
}
}

/**
* Build a text prompt from OpenAI chat messages + tool definitions.
* Handles role:"tool" result messages and assistant tool_calls that
* plain text flattening would silently drop.
*/
export function buildPromptFromMessages(messages: Array<any>, tools: Array<any>, subagentNames: string[] = []): string {
// DEBUG: Log incoming message structure to file for root cause analysis
const messageSummary = messages.map((m: any, i: number) => {
const role = m?.role ?? "?";
const hasToolCalls = Array.isArray(m?.tool_calls) ? m.tool_calls.length : 0;
Expand All @@ -52,7 +22,7 @@ export function buildPromptFromMessages(messages: Array<any>, tools: Array<any>,
const assistantEmpty = messages.filter((m: any) => m?.role === "assistant" && (!m?.tool_calls || m.tool_calls.length === 0) && (!m?.content || m.content === "" || m.content === null));
const toolResults = messages.filter((m: any) => m?.role === "tool");

debugLogToFile("buildPromptFromMessages", {
log.debug("buildPromptFromMessages", {
totalMessages: messages.length,
totalTools: tools.length,
messageSummary,
Expand Down Expand Up @@ -165,16 +135,15 @@ export function buildPromptFromMessages(messages: Array<any>, tools: Array<any>,
);
}

// DEBUG: Log the final prompt structure
const finalPrompt = lines.join("\n\n");
debugLogToFile("buildPromptFromMessages: final prompt", {
log.debug("buildPromptFromMessages: final prompt", {
lineCount: lines.length,
promptLength: finalPrompt.length,
promptPreview: finalPrompt.slice(0, 500),
hasToolResultFormat: finalPrompt.includes("TOOL_RESULT"),
hasAssistantToolCallFormat: finalPrompt.includes("tool_call(id:"),
hasCompletionSignal: finalPrompt.includes("Based on the tool results"),
hasCompletionSignal: finalPrompt.includes("The above tool calls have been executed"),
});

return finalPrompt;
}
}
Loading