diff --git a/src/mcp/resources.ts b/src/mcp/resources.ts index 097b65de..ad551dac 100644 --- a/src/mcp/resources.ts +++ b/src/mcp/resources.ts @@ -37,7 +37,11 @@ function registerHealthResource(server: McpServer, deps: ResourceDependencies): }, async (): Promise => { const memoryHealth = deps.memory - ? await deps.memory.healthCheck().catch(() => ({ qdrant: false, ollama: false })) + ? await deps.memory.healthCheck().catch((err: unknown) => { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[mcp] health resource memory check failed: ${msg}`); + return { qdrant: false, ollama: false }; + }) : { qdrant: false, ollama: false }; const uptimeSeconds = Math.floor((Date.now() - deps.startedAt) / 1000); @@ -284,7 +288,11 @@ function registerMemoryRecentResource(server: McpServer, deps: ResourceDependenc }; } - const episodes = await deps.memory.recallEpisodes("recent activity", { limit: 10 }).catch(() => []); + const episodes = await deps.memory.recallEpisodes("recent activity", { limit: 10 }).catch((err: unknown) => { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[mcp] memory-recent recallEpisodes failed: ${msg}`); + return []; + }); return { contents: [ { @@ -318,7 +326,11 @@ function registerMemoryDomainResource(server: McpServer, deps: ResourceDependenc return { contents: [{ uri: uri.href, text: JSON.stringify({ facts: [], available: false }) }] }; } - const facts = await deps.memory.recallFacts(topic as string, { limit: 20 }).catch(() => []); + const facts = await deps.memory.recallFacts(topic as string, { limit: 20 }).catch((err: unknown) => { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[mcp] memory-domain recallFacts failed: ${msg}`); + return []; + }); return { contents: [ { diff --git a/src/mcp/tools-swe.ts b/src/mcp/tools-swe.ts index bdf75b5f..ccd21166 100644 --- a/src/mcp/tools-swe.ts +++ b/src/mcp/tools-swe.ts @@ -56,8 +56,9 @@ function registerCodebaseQuery(server: McpServer, deps: ToolDependencies): void timestamp: e.started_at, })); } - } catch { - // Memory unavailable, continue without it + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[mcp] codebase_query recallEpisodes failed: ${msg}`); } try { @@ -68,8 +69,9 @@ function registerCodebaseQuery(server: McpServer, deps: ToolDependencies): void confidence: f.confidence, })); } - } catch { - // Memory unavailable, continue without it + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[mcp] codebase_query recallFacts failed: ${msg}`); } } @@ -280,8 +282,9 @@ function registerRepoInfo(server: McpServer, deps: ToolDependencies): void { fact: f.natural_language, })); } - } catch { - // Continue without memory + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[mcp] repo_info recallFacts failed: ${msg}`); } } diff --git a/src/mcp/tools-universal.ts b/src/mcp/tools-universal.ts index 03062a8d..0884a979 100644 --- a/src/mcp/tools-universal.ts +++ b/src/mcp/tools-universal.ts @@ -257,7 +257,11 @@ async function searchMemoryCore( const results: Record = {}; if (options.memoryType === "all" || options.memoryType === "episodic") { - const episodes = await deps.memory.recallEpisodes(options.query, { limit: options.limit }).catch(() => []); + const episodes = await deps.memory.recallEpisodes(options.query, { limit: options.limit }).catch((err: unknown) => { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[mcp] memory_search recallEpisodes failed: ${msg}`); + return []; + }); const daysBack = options.daysBack; if (daysBack != null && daysBack > 0) { const cutoff = Date.now() - daysBack * 24 * 60 * 60 * 1000; @@ -273,10 +277,18 @@ async function searchMemoryCore( } } if (options.memoryType === "all" || options.memoryType === "semantic") { - results.facts = await deps.memory.recallFacts(options.query, { limit: options.limit }).catch(() => []); + results.facts = await deps.memory.recallFacts(options.query, { limit: options.limit }).catch((err: unknown) => { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[mcp] memory_search recallFacts failed: ${msg}`); + return []; + }); } if (options.memoryType === "all" || options.memoryType === "procedural") { - const proc = await deps.memory.findProcedure(options.query).catch(() => null); + const proc = await deps.memory.findProcedure(options.query).catch((err: unknown) => { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[mcp] memory_search findProcedure failed: ${msg}`); + return null; + }); results.procedures = proc ? [proc] : []; } diff --git a/src/onboarding/profiler.ts b/src/onboarding/profiler.ts index 72db5b5c..59f73cea 100644 --- a/src/onboarding/profiler.ts +++ b/src/onboarding/profiler.ts @@ -51,9 +51,14 @@ export type SlackProfileClient = { * All API calls are best-effort - failures degrade gracefully to null fields. */ export async function profileOwner(client: SlackProfileClient, ownerUserId: string): Promise { + const logWarn = (api: string) => (err: unknown) => { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[onboarding] Slack ${api} failed for ${ownerUserId}: ${msg}`); + return null; + }; const [userResult, teamResult, channelsResult] = await Promise.all([ - client.users.info({ user: ownerUserId }).catch(() => null), - client.team.info().catch(() => null), + client.users.info({ user: ownerUserId }).catch(logWarn("users.info")), + client.team.info().catch(logWarn("team.info")), client.users .conversations({ user: ownerUserId, @@ -61,7 +66,7 @@ export async function profileOwner(client: SlackProfileClient, ownerUserId: stri exclude_archived: true, limit: 100, }) - .catch(() => null), + .catch(logWarn("users.conversations")), ]); const user = userResult?.user; diff --git a/src/persona/llm-caller.ts b/src/persona/llm-caller.ts index 1b63d542..cc02d098 100644 --- a/src/persona/llm-caller.ts +++ b/src/persona/llm-caller.ts @@ -76,7 +76,9 @@ export function parseEnvelope(text: string): LlmTurnResult { let parsed: unknown; try { parsed = JSON.parse(json); - } catch { + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[first-hour] envelope JSON parse failed: ${msg}`); return { pulls_executed: [], saved_drafts: [], diff --git a/src/plugins/curated.ts b/src/plugins/curated.ts index 70289860..3e969c2b 100644 --- a/src/plugins/curated.ts +++ b/src/plugins/curated.ts @@ -37,7 +37,9 @@ export function loadCuratedOverlay(pathOverride?: string): CuratedOverlay { let mtimeMs: number; try { mtimeMs = statSync(path).mtimeMs; - } catch { + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[plugins] curated overlay stat failed (${path}): ${msg}`); return DEFAULT_OVERLAY; } diff --git a/src/scheduler/parse-with-sonnet.ts b/src/scheduler/parse-with-sonnet.ts index 72878d5e..104242d2 100644 --- a/src/scheduler/parse-with-sonnet.ts +++ b/src/scheduler/parse-with-sonnet.ts @@ -101,7 +101,9 @@ export async function parseJobDescription(description: string, deps: ParseDeps = const parsed = JobCreateInputSchema.safeParse(result.data); if (!parsed.success) return { ok: false, status: 422, error: GENERIC_ERROR }; return { ok: true, proposal: parsed.data, warnings: [] }; - } catch { + } catch (err: unknown) { + const detail = err instanceof Error ? err.message : String(err); + console.warn(`[scheduler] Sonnet parse failed: ${detail}`); return { ok: false, status: 422, error: GENERIC_ERROR }; } } diff --git a/src/scheduler/schedule.ts b/src/scheduler/schedule.ts index c1aa9e04..cca87461 100644 --- a/src/scheduler/schedule.ts +++ b/src/scheduler/schedule.ts @@ -25,7 +25,9 @@ export function computeNextRunAt(schedule: Schedule, afterMs: number = Date.now( try { const cron = new Cron(schedule.expr, { timezone: tz, mode: "5-part" }); return cron.nextRun(new Date(afterMs)); - } catch { + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[scheduler] Invalid cron "${schedule.expr}" (tz=${tz}): ${msg}`); return null; } } diff --git a/src/scheduler/service.ts b/src/scheduler/service.ts index 807fd231..ee937cd8 100644 --- a/src/scheduler/service.ts +++ b/src/scheduler/service.ts @@ -321,7 +321,10 @@ export class Scheduler { for (const cb of this.jobCompleteCallbacks) { try { cb(name, status); - } catch {} + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[scheduler] Job-complete callback error for "${name}": ${msg}`); + } } } diff --git a/src/secrets/crypto.ts b/src/secrets/crypto.ts index 540fc0f3..b314b2ea 100644 --- a/src/secrets/crypto.ts +++ b/src/secrets/crypto.ts @@ -39,7 +39,9 @@ export function getEncryptionKey(): Buffer { } cachedKey = buf; return cachedKey; - } catch { + } catch (err: unknown) { + const detail = err instanceof Error ? err.message : String(err); + console.warn(`[secrets] Could not load encryption key from ${keyPath}: ${detail}. Generating new key.`); const key = randomBytes(KEY_LENGTH); const dir = dirname(keyPath); if (!existsSync(dir)) { diff --git a/src/skills/storage.ts b/src/skills/storage.ts index d438b266..6b9f1b15 100644 --- a/src/skills/storage.ts +++ b/src/skills/storage.ts @@ -238,7 +238,9 @@ export function writeSkill(input: WriteInput, options: { mustExist: boolean }): } else { previousBody = prevRaw; } - } catch { + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[skills] Failed to read previous skill content for ${name}: ${msg}`); previousBody = null; } } else if (options.mustExist) { @@ -287,7 +289,9 @@ export function deleteSkill(name: string): DeleteResult { const prevRaw = readFileSync(file, "utf-8"); const prevParsed = parseFrontmatter(prevRaw); previousBody = prevParsed.ok ? prevParsed.parsed.body : prevRaw; - } catch { + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[skills] Failed to read previous skill content for ${name}: ${msg}`); previousBody = null; } try { diff --git a/src/subagents/storage.ts b/src/subagents/storage.ts index 1e603112..16091a40 100644 --- a/src/subagents/storage.ts +++ b/src/subagents/storage.ts @@ -217,7 +217,9 @@ export function writeSubagent(input: WriteInput, options: { mustExist: boolean } } else { previousBody = prevRaw; } - } catch { + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[subagents] Failed to read previous content for ${name}: ${msg}`); previousBody = null; } } else if (options.mustExist) { @@ -263,7 +265,9 @@ export function deleteSubagent(name: string): DeleteResult { const prevRaw = readFileSync(file, "utf-8"); const prevParsed = parseFrontmatter(prevRaw); previousBody = prevParsed.ok ? prevParsed.parsed.body : prevRaw; - } catch { + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[subagents] Failed to read previous content for ${name}: ${msg}`); previousBody = null; } try { diff --git a/src/ui/api/evolution.ts b/src/ui/api/evolution.ts index 875ab45d..9abccf28 100644 --- a/src/ui/api/evolution.ts +++ b/src/ui/api/evolution.ts @@ -125,7 +125,9 @@ function buildOverview(deps: EvolutionApiDeps): OverviewResponse { if (deps.queue) { try { poisonCount = deps.queue.listPoisonPile().length; - } catch { + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[evolution] Failed to read poison pile: ${msg}`); poisonCount = 0; } } @@ -207,7 +209,9 @@ function readFilePreview(configDir: string, relPath: string): { content: string; let size = 0; try { size = statSync(absolute).size; - } catch { + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[evolution] stat failed for ${relPath}: ${msg}`); size = 0; } try { @@ -215,7 +219,9 @@ function readFilePreview(configDir: string, relPath: string): { content: string; const cap = FILE_PREVIEW_BYTE_CAP; const sliced = raw.length <= cap ? raw : raw.subarray(0, cap); return { content: sliced.toString("utf-8"), size }; - } catch { + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[evolution] read failed for ${relPath}: ${msg}`); return { content: "", size }; } } diff --git a/src/ui/api/identity.ts b/src/ui/api/identity.ts index c865bb35..bfea2936 100644 --- a/src/ui/api/identity.ts +++ b/src/ui/api/identity.ts @@ -54,7 +54,9 @@ function readMetaSync(): AvatarMeta | null { const parsed = JSON.parse(text) as AvatarMeta; if (!parsed || typeof parsed.ext !== "string" || typeof parsed.mime !== "string") return null; return parsed; - } catch { + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[identity] Failed to read avatar meta: ${msg}`); return null; } } @@ -173,7 +175,10 @@ export async function handleAvatarPost(req: Request): Promise { } catch (err: unknown) { try { if (existsSync(tmpFile)) unlinkSync(tmpFile); - } catch {} + } catch (cleanupErr: unknown) { + const cleanupMsg = cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr); + console.warn(`[identity] Failed to clean up tmp avatar file: ${cleanupMsg}`); + } const msg = err instanceof Error ? err.message : String(err); return errJson(`Avatar write failed: ${msg}`, 500); } @@ -184,7 +189,10 @@ export async function handleAvatarPost(req: Request): Promise { } catch (err: unknown) { try { if (existsSync(tmpMeta)) unlinkSync(tmpMeta); - } catch {} + } catch (cleanupErr: unknown) { + const cleanupMsg = cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr); + console.warn(`[identity] Failed to clean up tmp meta file: ${cleanupMsg}`); + } const msg = err instanceof Error ? err.message : String(err); return errJson(`Avatar meta write failed: ${msg}`, 500); } @@ -196,7 +204,10 @@ export async function handleAvatarPost(req: Request): Promise { if (entry.endsWith(".tmp")) continue; try { unlinkSync(resolve(dir, entry)); - } catch {} + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[identity] Failed to prune old avatar ${entry}: ${msg}`); + } } return Response.json({ ok: true, url: "/ui/avatar", size: bytes.byteLength, mime }); @@ -209,7 +220,10 @@ export function handleAvatarDelete(): Response { if (!entry.startsWith("avatar.")) continue; try { unlinkSync(resolve(dir, entry)); - } catch {} + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[identity] Failed to delete avatar file ${entry}: ${msg}`); + } } return new Response(null, { status: 204 }); } diff --git a/src/ui/api/pages.ts b/src/ui/api/pages.ts index 684ed97f..8d73af4b 100644 --- a/src/ui/api/pages.ts +++ b/src/ui/api/pages.ts @@ -156,7 +156,10 @@ async function extractTitle(absolutePath: string, rel: string): Promise return decoded.slice(0, MAX_TITLE_LEN); } } - } catch {} + } catch (err: unknown) { + const msg = err instanceof Error ? err.message : String(err); + console.warn(`[pages] Failed to extract title from ${rel}: ${msg}`); + } return filenameTitle(rel).slice(0, MAX_TITLE_LEN); }