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
12 changes: 10 additions & 2 deletions src/agentic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { createWorkspaceTools, createExecuteTool } from "./think-adapter";
import { createShellTool } from "./tools/shell";
import { sanitizeToolJsonSchema } from "./tool-schema";
import { runTypecheck } from "./typecheck";
import { log } from "./logger";
import type { AppConfig, Env, TodoStore } from "./types";

/** Options passed through from the coding agent into tool factories. */
Expand Down Expand Up @@ -1817,7 +1818,10 @@ function buildOAuthMcpTools(

for (const info of oauthTools) {
if (!info.name || !info.serverId) {
console.warn("[oauth-mcp] Skipping tool with missing name or serverId:", info);
log("warn", "oauth-mcp skipping tool with missing name or serverId", {
serverId: info.serverId,
name: info.name,
});
continue;
}

Expand All @@ -1827,7 +1831,11 @@ function buildOAuthMcpTools(
const prefixedName = fullName.length > 64 ? fullName.slice(0, 64) : fullName;

if (existingNames.has(prefixedName) || tools[prefixedName]) {
console.warn("[oauth-mcp] Skipping duplicate tool name:", { prefixedName, serverId: info.serverId, original: info.name });
log("warn", "oauth-mcp skipping duplicate tool name", {
prefixedName,
serverId: info.serverId,
original: info.name,
});
continue;
}

Expand Down
48 changes: 33 additions & 15 deletions src/coding-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2928,7 +2928,10 @@ export class CodingAgent extends Think<Env, DodoConfig> {
}
} else if (existing.length > 1) {
// Multiple sessions — use most recent, delete extras
console.warn(`CodingAgent: found ${existing.length} Think sessions, expected 1. Using most recent.`);
log("warn", "multiple think sessions found, using most recent", {
sessionId: this.sessionId(),
count: existing.length,
});
const sorted = [...existing].sort((a, b) => b.updated_at.localeCompare(a.updated_at));
// Switch to the most recent
this.switchSession(sorted[0].id);
Expand Down Expand Up @@ -6157,7 +6160,7 @@ export class CodingAgent extends Think<Env, DodoConfig> {
// returns an error. Think's applyChunkToParts silently drops these.
// Capture so we can throw after the stream ends.
streamError = chunk.errorText ?? chunk.error ?? "Unknown LLM error";
console.error("[runThinkChat] stream error chunk:", streamError);
log("error", "stream error chunk", { sessionId: this.sessionId(), error: streamError });
}
// Token usage is captured via onChatMessage() override — see _lastUsage.
} catch {
Expand All @@ -6178,7 +6181,7 @@ export class CodingAgent extends Think<Env, DodoConfig> {
}
},
onError: (error: string) => {
console.error("Think chat error:", error);
log("error", "think chat error", { sessionId: this.sessionId(), error });
streamError = error;
},
};
Expand Down Expand Up @@ -6305,7 +6308,10 @@ export class CodingAgent extends Think<Env, DodoConfig> {
try {
await this.maybeCompactContext();
} catch (err) {
console.warn("[compaction] Post-chat compaction failed:", err instanceof Error ? err.message : err);
log("warn", "post-chat compaction failed", {
sessionId: this.sessionId(),
error: err instanceof Error ? err.message : String(err),
});
}

return { assistantMessageId, tokenInput, tokenOutput, text: fullText };
Expand Down Expand Up @@ -6654,10 +6660,11 @@ export class CodingAgent extends Think<Env, DodoConfig> {
iterative: !!previousSummary,
});
} catch (error) {
console.warn(
"[compaction:ERROR] Failed to generate summary:",
error instanceof Error ? `${error.message}\n${error.stack}` : error,
);
log("warn", "failed to generate compaction summary", {
sessionId: this.sessionId(),
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
});
}
}

Expand Down Expand Up @@ -6963,7 +6970,10 @@ export class CodingAgent extends Think<Env, DodoConfig> {
this._artifactsTokenSecret = tokenSecret;
return { repo, remote, tokenSecret };
} catch (err) {
console.warn("[artifacts] failed to get/create repo:", err);
log("warn", "failed to get/create artifacts repo", {
sessionId: this.sessionId(),
error: err instanceof Error ? err.message : String(err),
});
return null;
}
}
Expand Down Expand Up @@ -7458,10 +7468,12 @@ export class CodingAgent extends Think<Env, DodoConfig> {
// We also record the failure on `mcpStatus` so the UI can surface it
// instead of leaving the user to guess why tools never appeared.
const message = error instanceof Error ? error.message : String(error);
console.warn(
`MCP connect failed for "${config.name}" (${config.id}):`,
message,
);
log("warn", "mcp connect failed", {
sessionId: this.sessionId(),
mcpId: config.id,
mcpName: config.name,
error: message,
});
this.mcpStatus.set(config.id, {
name: config.name,
url: config.url,
Expand All @@ -7485,7 +7497,10 @@ export class CodingAgent extends Think<Env, DodoConfig> {
// fingerprint/timestamp in place so the next call retries.
this.mcpConnectedAt = 0;
this.mcpEnabledConfigsFingerprint = null;
console.warn("connectMcpServers failed:", error instanceof Error ? error.message : error);
log("warn", "connectMcpServers failed", {
sessionId: this.sessionId(),
error: error instanceof Error ? error.message : String(error),
});
}
}

Expand Down Expand Up @@ -7547,7 +7562,10 @@ export class CodingAgent extends Think<Env, DodoConfig> {
});
} catch (error) {
// Log but don't throw — sync failure shouldn't break prompt completion
console.error("syncSessionIndex failed:", error instanceof Error ? error.message : error);
log("warn", "syncSessionIndex failed", {
sessionId: this.sessionId(),
error: error instanceof Error ? error.message : String(error),
});
}
}

Expand Down
28 changes: 25 additions & 3 deletions src/github-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,20 @@ export async function triggerVerifyWorkflow(input: {

if (!triggerRes.ok) {
const errorText = await triggerRes.text();
console.warn(`[verify-gate] workflow_dispatch failed ${triggerRes.status}:`, errorText.slice(0, 500));
let message = "";
try {
const parsed = JSON.parse(errorText) as { message?: unknown };
if (typeof parsed.message === "string") message = parsed.message;
} catch {
// Non-JSON error body — omit rather than forward raw text to the log sink.
}
log("warn", "verify-gate workflow_dispatch failed", {
runId: run.id,
repo: run.repoUrl,
branch: run.branch,
status: triggerRes.status,
error: message,
});
return null;
}

Expand All @@ -520,7 +533,11 @@ export async function triggerVerifyWorkflow(input: {
}
}

console.warn("[verify-gate] workflow dispatched but run id not found within 5s");
log("warn", "verify-gate workflow dispatched but run id not found", {
runId: run.id,
repo: run.repoUrl,
branch: run.branch,
});
return null;
}

Expand Down Expand Up @@ -564,7 +581,12 @@ export async function pollVerifyWorkflow(input: {
const url = `https://api.github.com/repos/${parsed.owner}/${parsed.repo}/actions/runs/${encodeURIComponent(run.verifyWorkflowRunId)}`;
const res = await fetch(url, { headers: ghHeaders(token) });
if (!res.ok) {
console.warn(`[verify-gate] poll failed ${res.status}`);
log("warn", "verify-gate poll failed", {
runId: run.id,
workflowRunId: run.verifyWorkflowRunId,
repo: run.repoUrl,
status: res.status,
});
return null;
}

Expand Down
8 changes: 5 additions & 3 deletions src/mcp-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
// outputSchema, which is why their MCP server's tools never appeared
// despite a working connect/initialize.)
import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/cfworker-provider.js";
import { log } from "./logger";

// ─── Auth header normalisation ───

Expand Down Expand Up @@ -140,9 +141,10 @@ export class HttpMcpClient implements McpClient {
const norm = normaliseAuthHeaders(headers);
headers = norm.headers;
if (norm.normalised) {
console.info(
`[mcp] prepended "Bearer " to Authorization header for "${this.config.name}" (${this.config.id}) — stored value was missing an auth scheme`,
);
log("info", "prepended Bearer auth scheme to MCP Authorization header", {
mcpId: this.config.id,
mcpName: this.config.name,
});
}
// Propagate MCP recursion depth to outbound MCP servers
if (this.mcpDepth > 0) {
Expand Down
24 changes: 20 additions & 4 deletions src/mcp-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { canonicalizeEmail, resolveAdminEmail } from "./auth";
import { chatMonitorIdName, sendChatReaction, sendChatReply } from "./chat-monitor-agent";
import { log } from "./logger";
import type { Env } from "./types";

/**
Expand All @@ -20,7 +21,7 @@ export function mcpUserEmail(env: Env, userEmail?: string, label = "mcp"): strin
if (canonical) return canonical;
const email = resolveAdminEmail(env);
if (!email) throw new Error("ADMIN_EMAIL must be configured for MCP access. Set it as a secret or in wrangler.jsonc vars.");
console.warn(`[${label}] Operation attributed to admin via service-mode fallback (no userEmail threaded).`);
log("warn", "mcp operation attributed to admin via service-mode fallback", { label });
return email;
}

Expand Down Expand Up @@ -89,7 +90,12 @@ export function registerChatReplyTool(server: McpServer, env: Env, userEmail: st
emoji: reactionEmoji,
action: "remove",
}).catch((err) => {
console.warn("[chat_reply] failed to remove loading reaction (non-fatal):", err instanceof Error ? err.message : String(err));
log("warn", "chat_reply failed to remove loading reaction", {
sessionId,
spaceId: flag.spaceId,
messageName,
error: err instanceof Error ? err.message : String(err),
});
});

// Add the done reaction only when we actually posted a reply.
Expand All @@ -99,7 +105,12 @@ export function registerChatReplyTool(server: McpServer, env: Env, userEmail: st
emoji: doneEmoji,
action: "add",
}).catch((err) => {
console.warn("[chat_reply] failed to add done reaction (non-fatal):", err instanceof Error ? err.message : String(err));
log("warn", "chat_reply failed to add done reaction", {
sessionId,
spaceId: flag.spaceId,
messageName,
error: err instanceof Error ? err.message : String(err),
});
});
}

Expand All @@ -113,7 +124,12 @@ export function registerChatReplyTool(server: McpServer, env: Env, userEmail: st
body: JSON.stringify({ messageName }),
});
} catch (err) {
console.warn("[chat_reply] failed to notify monitor of reaction clear (non-fatal):", err instanceof Error ? err.message : String(err));
log("warn", "chat_reply failed to notify monitor of reaction clear", {
sessionId,
spaceId: flag.spaceId,
messageName,
error: err instanceof Error ? err.message : String(err),
});
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,11 @@ export function createDodoMcpServer(env: Env, userEmail: string, depth = 0): Mcp
await sourceCtx.repo.fork(`dodo-${newId}`, { defaultBranchOnly: false });
}
} catch (err) {
console.warn("[fork_session] Artifacts fork failed (files still copied via snapshot):", err);
log("warn", "fork_session artifacts fork failed", {
sessionId,
newId,
error: err instanceof Error ? err.message : String(err),
});
}
return textResult({ sessionId: newId, sourceSessionId: sessionId, forkedAt: new Date().toISOString() });
});
Expand Down
13 changes: 9 additions & 4 deletions src/skill-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
*/

import type { Workspace } from "@cloudflare/shell";
import { log } from "./logger";

// ─── Types ───

Expand Down Expand Up @@ -221,9 +222,10 @@ export function normalizeFrontmatter(
if (options.dirName && options.dirName !== name) {
// Loose match — log a warning but don't throw. OpenCode is strict here;
// we're more forgiving so imported third-party skills don't fail.
console.warn(
`SKILL.md name "${name}" doesn't match directory "${options.dirName}" — proceeding anyway`,
);
log("warn", "skill name mismatch with directory", {
name,
dirName: options.dirName,
});
}

const descRaw = frontmatter.description;
Expand Down Expand Up @@ -434,7 +436,10 @@ async function loadWorkspaceSkill(
rawFrontmatter: input.rawFrontmatter,
};
} catch (error) {
console.warn(`skill-load-failed dir=${dir}:`, error instanceof Error ? error.message : error);
log("warn", "skill-load-failed", {
dir,
error: error instanceof Error ? error.message : String(error),
});
return null;
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/user-control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2999,7 +2999,9 @@ export class UserControl extends DurableObject<Env> {
const sharedIndex = this.env.SHARED_INDEX.get(this.env.SHARED_INDEX.idFromName("global"));
await sharedIndex.fetch(`https://shared-index/mcp-token-index/${encodeURIComponent(token)}`, { method: "DELETE" });
} catch (err) {
console.warn("[user-control] Failed to delete token from SharedIndex:", err);
log("warn", "failed to delete token from SharedIndex", {
error: err instanceof Error ? err.message : String(err),
});
}
return true;
}
Expand Down
Loading