refactor(errors): surface 57 deferred catch-and-swallow sites via logger
The earlier sweep cleaned the hot path (worker/runtime/automation/api-
server/ipc core — 59 sites). This pass finishes the deferred long tail:
main/* services, database/* CRUD modules, api-server/* routes, ipc-*
handlers, thread-integration, automation helpers, browser, skills.
Default strategy: catch (error) { logger.warn("[module] context:", ...) }
— preserve current behavior, make the failure visible instead of
silently hidden. Widened 3 logger interface signatures to include warn
where callers only passed info/error previously (thread-helpers,
host-environment-service, ipc-domain-common).
Legitimate boundaries left intact: JSON.parse on untrusted input, DNS
resolver no-records paths, explicit safe* helpers (safeJson, parseJson,
parseTriggerDetail), URL-parse predicates, and `<unserialisable>`
fallbacks inside the prompt-builder summarisers. Each kept catch now
either has a reason in context or is a documented safe* helper.
Tests adjusted only where test-constructed no-op loggers needed a warn
stub (message-dispatch, company-mode-switch, ipc-agent-graph-handlers).
With the earlier pass, AgentCompany no longer has silently-swallowing
catch blocks in main-process or renderer business code.
This commit is contained in:
1 parent
ecd1bc2e33
commit
40040b3dd2
46 files changed
+209
-113
No files matched your search
@@ -266,7 +266,8 @@ export class RuntimeBridge {
|
||||
let task: { title: string } | null = null;
|
||||
try {
|
||||
task = this.deps.db.getTask(envelope.taskId);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
this.deps.logger.warn(`[runtime-bridge] getTask failed for ${envelope.taskId} in recordThreadEvent:`, err instanceof Error ? err.message : String(err));
|
||||
return;
|
||||
}
|
||||
if (!task) return;
|
||||
|
||||
@@ -13,7 +13,8 @@ export function registerAgentControlRoutes(server: AgentApiServer) {
|
||||
const targetAgent = (() => {
|
||||
try {
|
||||
return db.getAgent(targetAgentId);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[api/agent-control] getAgent failed for ${targetAgentId}:`, err instanceof Error ? err.message : String(err));
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
@@ -52,7 +53,8 @@ export function registerAgentControlRoutes(server: AgentApiServer) {
|
||||
let targetAgent;
|
||||
try {
|
||||
targetAgent = db.getAgent(targetAgentId);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[api/agent-control] getAgent failed for heartbeat ${targetAgentId}:`, err instanceof Error ? err.message : String(err));
|
||||
targetAgent = null;
|
||||
}
|
||||
if (!targetAgent || targetAgent.companyId !== token.companyId) {
|
||||
|
||||
@@ -156,7 +156,8 @@ export function registerAgentRoutes(server: AgentApiServer) {
|
||||
let agent;
|
||||
try {
|
||||
agent = db.getAgent(requireParam(params, "agentId"));
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[api/agents] getAgent failed:`, err instanceof Error ? err.message : String(err));
|
||||
server.json(res, 404, { error: "Agent not found" });
|
||||
return;
|
||||
}
|
||||
@@ -262,7 +263,8 @@ export function registerAgentRoutes(server: AgentApiServer) {
|
||||
const connectorRecord = (() => {
|
||||
try {
|
||||
return db.getConnector(input.connectorId);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[api/agents] getConnector failed for ${input.connectorId}:`, err instanceof Error ? err.message : String(err));
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
@@ -305,7 +307,8 @@ export function registerAgentRoutes(server: AgentApiServer) {
|
||||
? (() => {
|
||||
try {
|
||||
return db.getAgent(resolvedReportsTo).name;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[api/agents] getAgent failed for manager ${resolvedReportsTo}:`, err instanceof Error ? err.message : String(err));
|
||||
return resolvedReportsTo;
|
||||
}
|
||||
})()
|
||||
|
||||
@@ -38,7 +38,8 @@ export function registerMeetingRoutes(server: AgentApiServer) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[api/meetings] participantAgentIds JSON parse failed:`, err instanceof Error ? err.message : String(err));
|
||||
server.json(res, 400, { error: "participantAgentIds must be a JSON array of agent ids" });
|
||||
return;
|
||||
}
|
||||
@@ -95,7 +96,8 @@ export function registerMeetingRoutes(server: AgentApiServer) {
|
||||
if (data.actionItemsJson) {
|
||||
try {
|
||||
actionItems = JSON.parse(data.actionItemsJson) as Array<{ title?: string; assigneeAgentId?: string; description?: string }>;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[api/meetings] actionItemsJson parse failed:`, err instanceof Error ? err.message : String(err));
|
||||
server.json(res, 400, { error: "actionItemsJson must be valid JSON" });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import log from "electron-log/main.js";
|
||||
import { agentMessageInputSchema } from "@shared/contracts";
|
||||
import { dispatchAgentMessage } from "../message-dispatch";
|
||||
import { requireParam, type AgentApiServer } from "./server-core";
|
||||
@@ -59,7 +60,8 @@ export function registerMessageRoutes(server: AgentApiServer) {
|
||||
}
|
||||
|
||||
server.json(res, 201, comment);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[api/messages] addComment failed:`, err instanceof Error ? err.message : String(err));
|
||||
server.json(res, 404, { error: "Task not found" });
|
||||
}
|
||||
});
|
||||
@@ -71,7 +73,8 @@ export function registerMessageRoutes(server: AgentApiServer) {
|
||||
server.json(res, 404, { error: "Task not found" });
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[api/messages] getTask failed for comments list:`, err instanceof Error ? err.message : String(err));
|
||||
server.json(res, 404, { error: "Task not found" });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,8 @@ export function registerTaskRoutes(server: AgentApiServer) {
|
||||
return;
|
||||
}
|
||||
server.json(res, 200, task);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[api/tasks] getTask failed:`, err instanceof Error ? err.message : String(err));
|
||||
server.json(res, 404, { error: "Task not found" });
|
||||
}
|
||||
});
|
||||
@@ -73,7 +74,8 @@ export function registerTaskRoutes(server: AgentApiServer) {
|
||||
let task;
|
||||
try {
|
||||
task = db.getTask(requireParam(params, "issueId"));
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[api/tasks] getTask failed for checkout:`, err instanceof Error ? err.message : String(err));
|
||||
server.json(res, 404, { error: "Task not found" });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import log from "electron-log/main.js";
|
||||
import { z } from "zod";
|
||||
import { goalInputSchema, projectInputSchema } from "@shared/contracts";
|
||||
import { canManageGoal, canManageProject } from "../agent-permissions";
|
||||
@@ -102,7 +103,8 @@ export function registerWorkGraphRoutes(server: AgentApiServer) {
|
||||
});
|
||||
onDomainChanged();
|
||||
server.json(res, 200, { ok: true });
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[api/work-graph] Failed to update project:`, err instanceof Error ? err.message : String(err));
|
||||
server.json(res, 500, { error: "Failed to update project" });
|
||||
}
|
||||
});
|
||||
@@ -187,7 +189,8 @@ export function registerWorkGraphRoutes(server: AgentApiServer) {
|
||||
});
|
||||
onDomainChanged();
|
||||
server.json(res, 200, { ok: true });
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[api/work-graph] Failed to update goal:`, err instanceof Error ? err.message : String(err));
|
||||
server.json(res, 500, { error: "Failed to update goal" });
|
||||
}
|
||||
});
|
||||
@@ -241,7 +244,8 @@ export function registerWorkGraphRoutes(server: AgentApiServer) {
|
||||
});
|
||||
onDomainChanged();
|
||||
server.json(res, 201, { id });
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[api/work-graph] Invalid sprint data:`, err instanceof Error ? err.message : String(err));
|
||||
server.json(res, 400, { error: "Invalid sprint data" });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -151,7 +151,8 @@ export function executeAutomationAction(
|
||||
try {
|
||||
const sender = db.getAgent(fromAgentId);
|
||||
channelTargetId = sender.department ?? null;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[automation] getAgent failed for sender ${fromAgentId}; dropping department target:`, err instanceof Error ? err.message : String(err));
|
||||
channelTargetId = null;
|
||||
}
|
||||
}
|
||||
@@ -221,21 +222,22 @@ export function executeAutomationAction(
|
||||
let agentName = "an agent";
|
||||
try {
|
||||
agentName = db.getAgent(targetAgentId).name;
|
||||
} catch {
|
||||
// Fall back to the generic label when the agent was deleted mid-flight.
|
||||
} catch (err) {
|
||||
logger.warn(`[automation] getAgent failed for escalation target ${targetAgentId}; using generic label:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
let taskTitle = ctx.taskId ?? "unknown";
|
||||
try {
|
||||
if (ctx.taskId) taskTitle = db.getTask(ctx.taskId).title;
|
||||
} catch {
|
||||
// Keep the fallback title when the task no longer exists.
|
||||
} catch (err) {
|
||||
logger.warn(`[automation] getTask failed for escalation task ${ctx.taskId}; using id as title:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
// Heartbeat tasks are internal system tasks — skip escalation messages to avoid flooding channels.
|
||||
let isHeartbeatTask = false;
|
||||
if (ctx.taskId) {
|
||||
try {
|
||||
isHeartbeatTask = db.getTask(ctx.taskId).isHeartbeat === true;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[automation] getTask failed for heartbeat check on ${ctx.taskId}; treating as non-heartbeat:`, err instanceof Error ? err.message : String(err));
|
||||
isHeartbeatTask = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import type { AppDatabase } from "../database";
|
||||
import type { AutomationEventContext } from "./types";
|
||||
|
||||
type ConditionLogger = {
|
||||
warn: (...args: unknown[]) => void;
|
||||
};
|
||||
|
||||
export function matchesEventConfig(
|
||||
db: AppDatabase,
|
||||
configJson: string,
|
||||
sourceDepartment: string | null,
|
||||
ctx: AutomationEventContext,
|
||||
logger?: ConditionLogger,
|
||||
): boolean {
|
||||
if (sourceDepartment && ctx.department && sourceDepartment !== ctx.department) {
|
||||
return false;
|
||||
@@ -21,8 +26,8 @@ export function matchesEventConfig(
|
||||
const task = ctx.taskId ? db.getTask(ctx.taskId) : null;
|
||||
if (task && task.priority !== conditions.priority) return false;
|
||||
// If task was deleted, skip this condition instead of rejecting the rule.
|
||||
} catch {
|
||||
// Entity missing — treat as condition not applicable, continue matching.
|
||||
} catch (err) {
|
||||
logger?.warn(`[automation-conditions] getTask failed for ${ctx.taskId}; treating priority condition as not applicable:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
if (typeof conditions.budgetThresholdPct === "number" && ctx.agentId) {
|
||||
@@ -33,8 +38,8 @@ export function matchesEventConfig(
|
||||
if (utilization < (conditions.budgetThresholdPct as number) / 100) return false;
|
||||
}
|
||||
// If budget is 0 (unlimited), skip budget condition instead of rejecting.
|
||||
} catch {
|
||||
// Agent missing — treat as condition not applicable, continue matching.
|
||||
} catch (err) {
|
||||
logger?.warn(`[automation-conditions] getAgent failed for ${ctx.agentId}; treating budget condition as not applicable:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
if (conditions.taskId && ctx.taskId && conditions.taskId !== ctx.taskId) return false;
|
||||
@@ -47,6 +52,7 @@ export function matchesConditions(
|
||||
db: AppDatabase,
|
||||
rule: { conditionsJson: string; sourceDepartment: string | null },
|
||||
ctx: AutomationEventContext,
|
||||
logger?: ConditionLogger,
|
||||
): boolean {
|
||||
return matchesEventConfig(db, rule.conditionsJson, rule.sourceDepartment, ctx);
|
||||
return matchesEventConfig(db, rule.conditionsJson, rule.sourceDepartment, ctx, logger);
|
||||
}
|
||||
@@ -102,8 +102,8 @@ export function handleTaskStatusChange(
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Parent task may have been deleted or reassigned during completion cascade evaluation.
|
||||
} catch (err) {
|
||||
deps.logger.warn(`[automation] Parent task ${task.parentId} lookup failed during completion cascade:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,8 +274,8 @@ export function processRunCompletion(
|
||||
try {
|
||||
const agent = db.getAgent(run.agentId);
|
||||
automationContext.department = agent.department ?? undefined;
|
||||
} catch {
|
||||
// Agent can disappear after a run completes; keep the context without department.
|
||||
} catch (err) {
|
||||
deps.logger.warn(`[automation] getAgent failed for ${run.agentId} during run completion; dropping department from context:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
|
||||
if (isSuccess) {
|
||||
@@ -295,8 +295,8 @@ export function processRunCompletion(
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Parent task may have been deleted or reassigned during cascade evaluation.
|
||||
} catch (err) {
|
||||
deps.logger.warn(`[automation] Parent task ${task.parentId} cascade failed during run completion:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,8 +326,8 @@ export function processRunCompletion(
|
||||
notify,
|
||||
(en, zh) => localizeNotification(budgetLocale, en, zh),
|
||||
);
|
||||
} catch {
|
||||
// Budget follow-up is best-effort; do not fail run completion if the agent disappeared.
|
||||
} catch (err) {
|
||||
deps.logger.warn(`[automation] Budget follow-up failed for agent ${run.agentId}:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,7 @@ export function createAutomationService({
|
||||
try {
|
||||
const rules = db.listActiveAutomationRules(ctx.companyId, trigger);
|
||||
for (const rule of rules) {
|
||||
if (!matchesConditions(db, rule, ctx)) continue;
|
||||
if (!matchesConditions(db, rule, ctx, logger)) continue;
|
||||
const executionKey = JSON.stringify({
|
||||
ruleId: rule.id,
|
||||
trigger,
|
||||
@@ -166,7 +166,7 @@ export function createAutomationService({
|
||||
try {
|
||||
const workflows = db.listTriggerableWorkflows(ctx.companyId, trigger);
|
||||
for (const workflow of workflows) {
|
||||
if (!matchesEventConfig(db, workflow.triggerConfigJson, null, ctx)) continue;
|
||||
if (!matchesEventConfig(db, workflow.triggerConfigJson, null, ctx, logger)) continue;
|
||||
const executionKey = JSON.stringify({
|
||||
workflowId: workflow.id,
|
||||
trigger,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { join } from "node:path";
|
||||
import log from "electron-log/main.js";
|
||||
import type { AppDatabase } from "./database";
|
||||
import { BrowserManager } from "./browser-manager";
|
||||
|
||||
@@ -28,7 +29,8 @@ export function createBrowserActionService({
|
||||
function getApprovalOrNull(approvalId: string): ReturnType<typeof db.getApprovalRecord> | null {
|
||||
try {
|
||||
return db.getApprovalRecord(approvalId);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[browser-action] getApprovalRecord failed for ${approvalId}:`, err instanceof Error ? err.message : String(err));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +236,8 @@ export class BrowserManager {
|
||||
rules.cookieOrigins.map(async (origin) => {
|
||||
try {
|
||||
return await ses.cookies.get({ url: origin });
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[browser-manager] cookies.get failed for origin ${origin}:`, err instanceof Error ? err.message : String(err));
|
||||
return [];
|
||||
}
|
||||
}),
|
||||
@@ -293,7 +294,8 @@ export class BrowserManager {
|
||||
})()
|
||||
`);
|
||||
return result && (result.accountName || result.displayName) ? result : { accountName: "", displayName: "" };
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[browser-manager] extractProfileFromSession evaluation failed:`, err instanceof Error ? err.message : String(err));
|
||||
return { accountName: "", displayName: "" };
|
||||
} finally {
|
||||
if (!win.isDestroyed()) win.close();
|
||||
|
||||
@@ -24,12 +24,17 @@ export interface BudgetGateResult {
|
||||
* Returns a human-readable label for a budget policy scope.
|
||||
* Looks up entity names from the database for agent and project scopes.
|
||||
*/
|
||||
function getScopeLabel(db: AppDatabase, policy: BudgetPolicyRecord): string {
|
||||
function getScopeLabel(
|
||||
db: AppDatabase,
|
||||
policy: BudgetPolicyRecord,
|
||||
logger: { warn: (...args: unknown[]) => void },
|
||||
): string {
|
||||
if (policy.scopeType === "agent") {
|
||||
try {
|
||||
const agent = db.getAgent(policy.scopeId);
|
||||
return `Agent "${agent.name}"`;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[budget] getAgent failed for scope ${policy.scopeId}:`, err instanceof Error ? err.message : String(err));
|
||||
return `Agent ${policy.scopeId.slice(0, 8)}`;
|
||||
}
|
||||
}
|
||||
@@ -37,7 +42,8 @@ function getScopeLabel(db: AppDatabase, policy: BudgetPolicyRecord): string {
|
||||
try {
|
||||
const row = db.queryAll<{ name: string }>("select name from projects where id = ?", policy.scopeId);
|
||||
return row.length > 0 ? `Project "${row[0].name}"` : `Project ${policy.scopeId.slice(0, 8)}`;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[budget] project name lookup failed for scope ${policy.scopeId}:`, err instanceof Error ? err.message : String(err));
|
||||
return `Project ${policy.scopeId.slice(0, 8)}`;
|
||||
}
|
||||
}
|
||||
@@ -106,7 +112,7 @@ export function createBudgetEnforcementService({
|
||||
const spent = db.getSpentInWindow(policy);
|
||||
const utilization = spent / policy.amount;
|
||||
const warnThreshold = policy.warnPercent / 100;
|
||||
const scopeLabel = getScopeLabel(db, policy);
|
||||
const scopeLabel = getScopeLabel(db, policy, logger);
|
||||
|
||||
if (utilization >= warnThreshold) {
|
||||
db.upsertBudgetIncident(policy, "warn_threshold", spent);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { homedir, tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import log from "electron-log/main.js";
|
||||
import type { ConnectorId } from "@shared/types";
|
||||
|
||||
function quoteShell(value: string) {
|
||||
@@ -79,8 +80,8 @@ function spawnTerminalWithScript(script: string, _title: string, _cwd: string):
|
||||
});
|
||||
child.unref();
|
||||
return true;
|
||||
} catch {
|
||||
// Try next terminal candidate.
|
||||
} catch (err) {
|
||||
log.warn(`[connector-auth] Failed to spawn terminal ${binary}; trying next candidate:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { DatabaseSync, SQLInputValue } from "node:sqlite";
|
||||
import log from "electron-log/main.js";
|
||||
import type { AgentRecord, AgentRoleLevel, ConnectorId } from "@shared/types";
|
||||
import {
|
||||
type ReadAll,
|
||||
@@ -125,8 +126,8 @@ export function buildAgentCrudMethods(
|
||||
configBefore: beforeConfig,
|
||||
configAfter: afterConfig,
|
||||
});
|
||||
} catch {
|
||||
/* Non-fatal: revision recording should never block agent save */
|
||||
} catch (err) {
|
||||
log.warn(`[database/agents] recordConfigRevision failed for agent ${id}:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { chmodSync, existsSync, mkdirSync } from "node:fs";
|
||||
import { copyFile, cp, rm } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { DatabaseSync, backup as backupDatabase } from "node:sqlite";
|
||||
import log from "electron-log/main.js";
|
||||
|
||||
async function copyIfExists(source: string, destination: string) {
|
||||
if (!existsSync(source)) {
|
||||
@@ -107,8 +108,8 @@ export function buildBackupMethods(
|
||||
try {
|
||||
deps.setDb(deps.openConnection());
|
||||
deps.init();
|
||||
} catch {
|
||||
// Reopen failed. Caller sees original + recovery messages.
|
||||
} catch (reopenError) {
|
||||
log.error(`[database/backup] Reopen failed after restore/recovery failure:`, reopenError instanceof Error ? reopenError.message : String(reopenError));
|
||||
}
|
||||
throw new Error(
|
||||
`Restore failed: ${message}. Recovery also failed: ${recoveryMessage}. Previous profile is at ${recoveryDir}.`,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { DatabaseSync } from "node:sqlite";
|
||||
import log from "electron-log/main.js";
|
||||
import type { ConnectorId, ConnectorRecord } from "@shared/types";
|
||||
import type { ReadAll } from "./helpers";
|
||||
import { nowIso } from "./helpers";
|
||||
@@ -30,7 +31,8 @@ export function buildConnectorMethods(
|
||||
let definition;
|
||||
try {
|
||||
definition = getConnectorDefinition(row.id as ConnectorId);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[database/connectors] Unknown connector id ${row.id}; skipping row:`, err instanceof Error ? err.message : String(err));
|
||||
continue;
|
||||
}
|
||||
result.push({
|
||||
@@ -110,7 +112,8 @@ export function buildConnectorMethods(
|
||||
let definition;
|
||||
try {
|
||||
definition = getConnectorDefinition(row.id as ConnectorId);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[database/connectors] Unknown connector id ${row.id} in findConnector:`, err instanceof Error ? err.message : String(err));
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import type { DatabaseSync, SQLInputValue } from "node:sqlite";
|
||||
import log from "electron-log/main.js";
|
||||
import type { AgentMessageRecord, AgentRecord } from "@shared/types";
|
||||
import {
|
||||
BOARD_READER_ID,
|
||||
@@ -139,7 +140,8 @@ export function buildMessageMethods(
|
||||
if (agent.companyId === companyId) {
|
||||
department = agent.department;
|
||||
}
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[database/messages] getAgent failed for ${opts.agentId}; department filter disabled:`, err instanceof Error ? err.message : String(err));
|
||||
department = null;
|
||||
}
|
||||
|
||||
@@ -277,7 +279,8 @@ export function buildMessageMethods(
|
||||
if (agent.companyId === companyId) {
|
||||
department = agent.department;
|
||||
}
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[database/messages] getAgent failed for ${agentId} in listAgentInboxUnread; department filter disabled:`, err instanceof Error ? err.message : String(err));
|
||||
department = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { writeFileSync, readFileSync, existsSync, statSync, openSync, readSync,
|
||||
import { appendFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import type { DatabaseSync, SQLInputValue } from "node:sqlite";
|
||||
import log from "electron-log/main.js";
|
||||
import type { ConnectorId, RunLogChunk, RunRecord } from "@shared/types";
|
||||
import { type ReadAll, type SqliteRow, type Transaction, nowIso, toBoolean } from "./helpers";
|
||||
|
||||
@@ -90,7 +91,8 @@ export function buildRunMethods(
|
||||
if (!run?.logPath) return "";
|
||||
try {
|
||||
return readFileSync(run.logPath, "utf8");
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[database/runs] readFileSync failed for run ${runId} log ${run.logPath}:`, err instanceof Error ? err.message : String(err));
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
session,
|
||||
shell,
|
||||
} from "electron";
|
||||
import log from "electron-log/main.js";
|
||||
import { attachWindowStatePersistence, loadWindowState } from "./window-state";
|
||||
import type { NotificationOptions } from "./notification-service";
|
||||
|
||||
@@ -96,8 +97,8 @@ export function createDesktopShellWindow(deps: DesktopShellWindowDependencies) {
|
||||
let activeRunCount = 0;
|
||||
try {
|
||||
activeRunCount = getActiveRunCount();
|
||||
} catch {
|
||||
// Cannot determine active runs — proceed with immediate quit as fail-safe
|
||||
} catch (err) {
|
||||
log.warn(`[desktop-shell-window] getActiveRunCount failed; quitting as fail-safe:`, err instanceof Error ? err.message : String(err));
|
||||
onRequestQuit();
|
||||
app.quit();
|
||||
return;
|
||||
@@ -150,8 +151,8 @@ export function createDesktopShellWindow(deps: DesktopShellWindowDependencies) {
|
||||
}
|
||||
}
|
||||
// Cancel: do nothing
|
||||
} catch {
|
||||
// Dialog failed (e.g. window destroyed mid-flight) — quit as fail-safe
|
||||
} catch (err) {
|
||||
log.warn(`[desktop-shell-window] Quit confirmation dialog failed; quitting as fail-safe:`, err instanceof Error ? err.message : String(err));
|
||||
onRequestQuit();
|
||||
app.quit();
|
||||
} finally {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import log from "electron-log/main.js";
|
||||
import { resolveApprovalDecision } from "./approval-policy";
|
||||
import type { AppDatabase } from "./database";
|
||||
|
||||
@@ -35,7 +36,8 @@ function collectImpactedAgentIdsFromWaitStates(db: AppDatabase, companyId: strin
|
||||
if (task.assigneeAgentId) {
|
||||
agentIds.add(task.assigneeAgentId);
|
||||
}
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[execution-mode-reconciler] getTask failed for ${thread.relatedEntityId}:`, err instanceof Error ? err.message : String(err));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -71,7 +73,8 @@ function collectImpactedAgentIdsFromReviewState(db: AppDatabase, companyId: stri
|
||||
if (task.assigneeAgentId) {
|
||||
agentIds.add(task.assigneeAgentId);
|
||||
}
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[execution-mode-reconciler] getTask failed for approval ${approval.relatedTaskId}:`, err instanceof Error ? err.message : String(err));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -87,7 +90,8 @@ function resumeAgents(db: AppDatabase, companyId: string, agentIds: Iterable<str
|
||||
if (!agent.heartbeatEnabled) {
|
||||
db.setHeartbeat(agent.id, true, agent.heartbeatIntervalSec || 120);
|
||||
}
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[execution-mode-reconciler] resumeAgents failed for ${agentId}:`, err instanceof Error ? err.message : String(err));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { app } from "electron";
|
||||
interface HostEnvironmentDependencies {
|
||||
logger: {
|
||||
info: (...args: unknown[]) => void;
|
||||
warn: (...args: unknown[]) => void;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,7 +30,8 @@ function enrichWindowsPath(logger: HostEnvironmentDependencies["logger"]) {
|
||||
return readdirSync(pythonBase)
|
||||
.filter((name) => /^Python\d+$/.test(name))
|
||||
.map((name) => join(pythonBase, name, "Scripts"));
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[path] readdirSync failed for ${pythonBase}:`, err instanceof Error ? err.message : String(err));
|
||||
return [];
|
||||
}
|
||||
})();
|
||||
@@ -94,8 +96,8 @@ export async function prepareHostEnvironment({
|
||||
logger.info(`[path] Added ${toAdd.length} shell PATH entries.`);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Non-fatal environment enrichment.
|
||||
} catch (err) {
|
||||
logger.warn(`[path] Shell PATH probe failed:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
} else {
|
||||
logger.info("[path] Skipping interactive shell PATH probe; using deterministic PATH setup only.");
|
||||
|
||||
@@ -107,8 +107,8 @@ export function registerBrowserHandlers({
|
||||
const profile = await browserActions.extractProfileFromSession(input.socialAccountId, account.platform);
|
||||
detectedName = profile.accountName;
|
||||
detectedDisplay = profile.displayName;
|
||||
} catch {
|
||||
// Profile extraction is best-effort; proceed with login status update
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc/browser] extractProfileFromSession failed for social account ${input.socialAccountId}:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
db.saveSocialAccount({
|
||||
id: latestAccount.id,
|
||||
|
||||
@@ -13,7 +13,7 @@ import type { AppDatabase } from "./database";
|
||||
|
||||
interface BudgetHandlerDependencies extends Pick<
|
||||
RegisterDomainHandlersDependencies,
|
||||
"registerHandle" | "ensureCompanyExists" | "publishDomainChanged"
|
||||
"registerHandle" | "ensureCompanyExists" | "publishDomainChanged" | "logger"
|
||||
> {
|
||||
budgetService: ReturnType<typeof createBudgetEnforcementService> | null;
|
||||
db: AppDatabase;
|
||||
@@ -25,6 +25,7 @@ export function registerBudgetHandlers({
|
||||
publishDomainChanged,
|
||||
budgetService,
|
||||
db,
|
||||
logger,
|
||||
}: BudgetHandlerDependencies) {
|
||||
// ---- Save (create/update) a budget policy ----
|
||||
registerHandle(IPC_CHANNELS.saveBudgetPolicy, async (_event, payload) => {
|
||||
@@ -36,8 +37,8 @@ export function registerBudgetHandlers({
|
||||
if (budgetService) {
|
||||
try {
|
||||
budgetService.checkAutoResolve(parsed.companyId, id);
|
||||
} catch {
|
||||
// Best effort -- service may not be ready
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc/budget] checkAutoResolve failed for policy ${id} in company ${parsed.companyId}:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
publishDomainChanged();
|
||||
|
||||
@@ -32,6 +32,7 @@ export function registerContentOperationsHandlers({
|
||||
publishDomainChanged,
|
||||
wakeAgentIfPossible,
|
||||
dispatchAutomation,
|
||||
logger,
|
||||
}: Pick<
|
||||
RegisterDomainHandlersDependencies,
|
||||
| "registerHandle"
|
||||
@@ -41,6 +42,7 @@ export function registerContentOperationsHandlers({
|
||||
| "publishDomainChanged"
|
||||
| "wakeAgentIfPossible"
|
||||
| "dispatchAutomation"
|
||||
| "logger"
|
||||
>) {
|
||||
registerHandle(IPC_CHANNELS.saveMeeting, async (_event, payload) => {
|
||||
const parsed = meetingInputSchema.parse(payload);
|
||||
@@ -52,7 +54,8 @@ export function registerContentOperationsHandlers({
|
||||
participantAgentIds = Array.isArray(parsedParticipantIds)
|
||||
? parsedParticipantIds.filter((value): value is string => typeof value === "string")
|
||||
: [];
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc/content] Meeting participants JSON parse failed:`, err instanceof Error ? err.message : String(err));
|
||||
return fail("INVALID_PARTICIPANTS", "Meeting participants must be valid JSON.");
|
||||
}
|
||||
const invalidReference = findInvalidReference(parsed.companyId, [
|
||||
@@ -163,7 +166,8 @@ export function registerContentOperationsHandlers({
|
||||
if (parsed.id) {
|
||||
try {
|
||||
previousSprintStatus = db.listSnapshot().sprints.find((s: { id: string }) => s.id === parsed.id)?.status ?? null;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc/content] listSnapshot failed while resolving sprint ${parsed.id} previous status:`, err instanceof Error ? err.message : String(err));
|
||||
previousSprintStatus = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ export function registerCoreAgentGraphHandlers({
|
||||
findInvalidReference,
|
||||
publishDomainChanged,
|
||||
handleApprovalResolved,
|
||||
logger,
|
||||
}: Pick<
|
||||
RegisterCoreHandlersDependencies,
|
||||
| "registerHandle"
|
||||
@@ -26,6 +27,7 @@ export function registerCoreAgentGraphHandlers({
|
||||
| "findInvalidReference"
|
||||
| "publishDomainChanged"
|
||||
| "handleApprovalResolved"
|
||||
| "logger"
|
||||
>) {
|
||||
registerHandle(IPC_CHANNELS.saveWorkspace, async (_event, payload) => {
|
||||
const parsed = workspaceInputSchema.parse(payload);
|
||||
@@ -182,7 +184,8 @@ export function registerCoreAgentGraphHandlers({
|
||||
? (() => {
|
||||
try {
|
||||
return db.getAgent(resolvedReportsTo).name;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc/agent-graph] getAgent failed for manager ${resolvedReportsTo}:`, err instanceof Error ? err.message : String(err));
|
||||
return resolvedReportsTo;
|
||||
}
|
||||
})()
|
||||
|
||||
@@ -32,6 +32,7 @@ export function registerCoreOperationHandlers({
|
||||
queueTaskRun,
|
||||
getWorkerProcess,
|
||||
notify,
|
||||
logger,
|
||||
}: Pick<
|
||||
RegisterCoreHandlersDependencies,
|
||||
| "registerHandle"
|
||||
@@ -46,6 +47,7 @@ export function registerCoreOperationHandlers({
|
||||
| "queueTaskRun"
|
||||
| "getWorkerProcess"
|
||||
| "notify"
|
||||
| "logger"
|
||||
>) {
|
||||
registerHandle(IPC_CHANNELS.requestApproval, async (_event, payload) => {
|
||||
const parsed = approvalInputSchema.parse(payload);
|
||||
@@ -201,8 +203,8 @@ export function registerCoreOperationHandlers({
|
||||
taskId: parsed.taskId,
|
||||
agentId: parsed.authorAgentId ?? undefined,
|
||||
});
|
||||
} catch {
|
||||
// Task may have been deleted between comment creation and wake evaluation.
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc/core-operations] Post-comment wake/dispatch failed for task ${parsed.taskId}:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
return ok(comment.id);
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ export function registerCoreReadHandlers({
|
||||
resetAfterRestore,
|
||||
getApiPort,
|
||||
listConnectorModels,
|
||||
logger,
|
||||
}: Pick<
|
||||
RegisterCoreHandlersDependencies,
|
||||
| "registerHandle"
|
||||
@@ -30,6 +31,7 @@ export function registerCoreReadHandlers({
|
||||
| "resetAfterRestore"
|
||||
| "getApiPort"
|
||||
| "listConnectorModels"
|
||||
| "logger"
|
||||
>) {
|
||||
registerHandle(IPC_CHANNELS.loadSnapshot, () => ok(db.listRendererSnapshot()));
|
||||
|
||||
@@ -109,7 +111,8 @@ export function registerCoreReadHandlers({
|
||||
try {
|
||||
const parsed = connectorInputSchema.shape.id.parse(connectorId);
|
||||
return ok(await listConnectorModels(parsed));
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc] listConnectorModels failed for connector ${String(connectorId)}:`, err instanceof Error ? err.message : String(err));
|
||||
return ok([]);
|
||||
}
|
||||
});
|
||||
@@ -118,7 +121,8 @@ export function registerCoreReadHandlers({
|
||||
try {
|
||||
const parsed = connectorInputSchema.shape.id.parse(connectorId);
|
||||
return ok(db.getDisabledModels(parsed));
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc] getConnectorDisabledModels failed for connector ${String(connectorId)}:`, err instanceof Error ? err.message : String(err));
|
||||
return ok([]);
|
||||
}
|
||||
});
|
||||
@@ -180,7 +184,8 @@ export function registerCoreReadHandlers({
|
||||
const task = db.getTask(parsed.id);
|
||||
if (task.companyId !== parsed.companyId) return fail("TASK_NOT_FOUND", "Task not found in current company.");
|
||||
return ok(task);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc] getTask failed for id ${parsed.id}:`, err instanceof Error ? err.message : String(err));
|
||||
return fail("TASK_NOT_FOUND", "Task not found.");
|
||||
}
|
||||
});
|
||||
@@ -193,7 +198,8 @@ export function registerCoreReadHandlers({
|
||||
const agent = db.getAgent(parsed.id);
|
||||
if (agent.companyId !== parsed.companyId) return fail("AGENT_NOT_FOUND", "Agent not found in current company.");
|
||||
return ok(agent);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc] getAgent failed for id ${parsed.id}:`, err instanceof Error ? err.message : String(err));
|
||||
return fail("AGENT_NOT_FOUND", "Agent not found.");
|
||||
}
|
||||
});
|
||||
@@ -206,7 +212,8 @@ export function registerCoreReadHandlers({
|
||||
const run = db.getRun(parsed.id);
|
||||
if (run.companyId !== parsed.companyId) return fail("RUN_NOT_FOUND", "Run not found in current company.");
|
||||
return ok(run);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc] getRun failed for id ${parsed.id}:`, err instanceof Error ? err.message : String(err));
|
||||
return fail("RUN_NOT_FOUND", "Run not found.");
|
||||
}
|
||||
});
|
||||
@@ -219,7 +226,8 @@ export function registerCoreReadHandlers({
|
||||
const approval = db.getApprovalRecord(parsed.id);
|
||||
if (approval.companyId !== parsed.companyId) return fail("APPROVAL_NOT_FOUND", "Approval not found in current company.");
|
||||
return ok(approval);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc] getApprovalRecord failed for id ${parsed.id}:`, err instanceof Error ? err.message : String(err));
|
||||
return fail("APPROVAL_NOT_FOUND", "Approval not found.");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -118,7 +118,8 @@ export function registerCoreTaskGraphHandlers({
|
||||
const existingTask = db.getTask(taskId);
|
||||
previousStatus = existingTask.status;
|
||||
previousAssigneeAgentId = existingTask.assigneeAgentId;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc/task-graph] getTask failed for ${taskId}; treating as new task:`, err instanceof Error ? err.message : String(err));
|
||||
previousStatus = null;
|
||||
previousAssigneeAgentId = null;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ export function registerCoreWorkGraphHandlers(
|
||||
findInvalidReference: deps.findInvalidReference,
|
||||
publishDomainChanged: deps.publishDomainChanged,
|
||||
handleApprovalResolved: deps.handleApprovalResolved,
|
||||
logger: deps.logger,
|
||||
});
|
||||
registerCoreTaskGraphHandlers({
|
||||
registerHandle: deps.registerHandle,
|
||||
|
||||
@@ -30,6 +30,7 @@ export interface RegisterDomainHandlersDependencies {
|
||||
) => void;
|
||||
db: AppDatabase;
|
||||
logger: {
|
||||
warn: (...args: unknown[]) => void;
|
||||
error: (...args: unknown[]) => void;
|
||||
};
|
||||
ensureCompanyExists: (companyId: string) => DesktopResult<never> | null;
|
||||
|
||||
@@ -21,6 +21,7 @@ export function registerDomainHandlers(dependencies: RegisterDomainHandlersDepen
|
||||
publishDomainChanged: dependencies.publishDomainChanged,
|
||||
skillService: dependencies.skillService,
|
||||
db: dependencies.db,
|
||||
logger: dependencies.logger,
|
||||
});
|
||||
registerBudgetHandlers({
|
||||
registerHandle: dependencies.registerHandle,
|
||||
@@ -28,5 +29,6 @@ export function registerDomainHandlers(dependencies: RegisterDomainHandlersDepen
|
||||
publishDomainChanged: dependencies.publishDomainChanged,
|
||||
budgetService: dependencies.budgetService,
|
||||
db: dependencies.db,
|
||||
logger: dependencies.logger,
|
||||
});
|
||||
}
|
||||
@@ -19,6 +19,7 @@ export function registerRecoveryHandlers({
|
||||
ensureCompanyExists,
|
||||
publishDomainChanged,
|
||||
wakeAgentIfPossible,
|
||||
logger,
|
||||
}: Pick<
|
||||
RegisterDomainHandlersDependencies,
|
||||
| "registerHandle"
|
||||
@@ -26,6 +27,7 @@ export function registerRecoveryHandlers({
|
||||
| "ensureCompanyExists"
|
||||
| "publishDomainChanged"
|
||||
| "wakeAgentIfPossible"
|
||||
| "logger"
|
||||
>) {
|
||||
registerHandle(IPC_CHANNELS.recoveryRetry, async (_event, payload) => {
|
||||
const parsed = recoveryRetrySchema.parse(payload);
|
||||
@@ -37,8 +39,8 @@ export function registerRecoveryHandlers({
|
||||
db.retryInterruptedTask(parsed.taskId, parsed.agentId, parsed.companyId);
|
||||
try {
|
||||
wakeAgentIfPossible(parsed.agentId, parsed.companyId, "retry_interrupted");
|
||||
} catch {
|
||||
// Best effort -- agent may have been deleted
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc/recovery] wakeAgentIfPossible failed for agent ${parsed.agentId}:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
publishDomainChanged();
|
||||
return ok(null);
|
||||
|
||||
@@ -16,7 +16,7 @@ import type { AppDatabase } from "./database";
|
||||
|
||||
interface SkillHandlerDependencies extends Pick<
|
||||
RegisterDomainHandlersDependencies,
|
||||
"registerHandle" | "ensureCompanyExists" | "publishDomainChanged"
|
||||
"registerHandle" | "ensureCompanyExists" | "publishDomainChanged" | "logger"
|
||||
> {
|
||||
skillService: ReturnType<typeof createSkillService> | null;
|
||||
db: AppDatabase;
|
||||
@@ -28,6 +28,7 @@ export function registerSkillHandlers({
|
||||
publishDomainChanged,
|
||||
skillService,
|
||||
db,
|
||||
logger,
|
||||
}: SkillHandlerDependencies) {
|
||||
function requireSkillService():
|
||||
| { service: NonNullable<typeof skillService>; error?: undefined }
|
||||
@@ -64,8 +65,8 @@ export function registerSkillHandlers({
|
||||
// Scan workspace .agents/skills/ directories for new skills before listing
|
||||
try {
|
||||
await svc.scanWorkspaceSkills(parsed.companyId);
|
||||
} catch {
|
||||
// Non-fatal: scan failure should not block listing existing skills
|
||||
} catch (err) {
|
||||
logger.warn(`[ipc/skills] scanWorkspaceSkills failed for company ${parsed.companyId}:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
return ok(svc.listForCompany(parsed.companyId));
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* other provider we delegate to pi-ai's `getModel` / `getModels`.
|
||||
*/
|
||||
|
||||
import log from "electron-log/main.js";
|
||||
import { getModel, getModels, type Api, type Model } from "@mariozechner/pi-ai";
|
||||
|
||||
const FIREWORKS_BASE_URL = "https://api.fireworks.ai/inference";
|
||||
@@ -54,7 +55,8 @@ export function resolveModel(provider: string, modelId: string): Model<Api> | nu
|
||||
}
|
||||
try {
|
||||
return getModel(provider as never, modelId as never) ?? null;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[model-resolver] getModel failed for ${provider}/${modelId}:`, err instanceof Error ? err.message : String(err));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -69,7 +71,8 @@ export function resolveModels(provider: string): Array<{ id: string; name: strin
|
||||
try {
|
||||
const models = getModels(provider as never) ?? [];
|
||||
return models.map((model: { id: string; name?: string }) => ({ id: model.id, name: model.name ?? model.id }));
|
||||
} catch {
|
||||
} catch (err) {
|
||||
log.warn(`[model-resolver] getModels failed for ${provider}:`, err instanceof Error ? err.message : String(err));
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,8 @@ export function createLifecycle(deps: LifecycleDeps) {
|
||||
return db.listSnapshot().runs
|
||||
.filter((r) => r.status === "running" || r.status === "queued")
|
||||
.map((r) => r.id);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn("[lifecycle] listActiveRunIds failed:", err instanceof Error ? err.message : String(err));
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,8 @@ export function createSessionManagementService({ db, logger }: SessionManagement
|
||||
let task: { title: string; description: string } | null = null;
|
||||
try {
|
||||
task = db.getTask(taskId);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[session-management] getTask failed for ${taskId} during handoff generation:`, err instanceof Error ? err.message : String(err));
|
||||
task = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -272,8 +272,8 @@ export function createSkillImporter({ db, profileDir: _profileDir, logger, skill
|
||||
const pathParts = urlObj.pathname.split("/").filter(Boolean);
|
||||
const lastPart = pathParts[pathParts.length - 1] ?? "";
|
||||
urlSlug = normalizeSkillSlug(lastPart.replace(/\.md$/i, ""));
|
||||
} catch {
|
||||
// ignore URL parsing errors
|
||||
} catch (err) {
|
||||
logger.warn(`[skill-import] Unable to parse skill URL for slug derivation (${url}):`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
const rawSlug =
|
||||
normalizeSkillSlug(parsed.frontmatter.slug as string | undefined) ??
|
||||
@@ -340,7 +340,8 @@ export function createSkillImporter({ db, profileDir: _profileDir, logger, skill
|
||||
let realDir: string;
|
||||
try {
|
||||
realDir = realpathSync(dir);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[skill-import] realpathSync failed for ${dir}:`, err instanceof Error ? err.message : String(err));
|
||||
return;
|
||||
}
|
||||
if (visitedRealPaths.has(realDir)) {
|
||||
@@ -351,7 +352,8 @@ export function createSkillImporter({ db, profileDir: _profileDir, logger, skill
|
||||
let entries: Dirent[];
|
||||
try {
|
||||
entries = readdirSync(dir, { withFileTypes: true });
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[skill-import] readdirSync failed for ${dir}:`, err instanceof Error ? err.message : String(err));
|
||||
return;
|
||||
}
|
||||
for (const entry of entries) {
|
||||
@@ -395,7 +397,8 @@ export function createSkillImporter({ db, profileDir: _profileDir, logger, skill
|
||||
let realDir: string;
|
||||
try {
|
||||
realDir = realpathSync(dir);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[skill-import] realpathSync failed for ${dir}:`, err instanceof Error ? err.message : String(err));
|
||||
return;
|
||||
}
|
||||
if (visitedRealPaths.has(realDir)) {
|
||||
@@ -406,7 +409,8 @@ export function createSkillImporter({ db, profileDir: _profileDir, logger, skill
|
||||
let entries: Dirent[];
|
||||
try {
|
||||
entries = readdirSync(dir, { withFileTypes: true });
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[skill-import] readdirSync failed for ${dir}:`, err instanceof Error ? err.message : String(err));
|
||||
return;
|
||||
}
|
||||
for (const entry of entries) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* minimum description quality, acceptance criteria, and capability matching.
|
||||
*/
|
||||
|
||||
import log from "electron-log/main.js";
|
||||
import type { DesktopEvent } from "@shared/types";
|
||||
import type { AppDatabase } from "@main/database";
|
||||
import type { NotificationOptions } from "@main/notification-service";
|
||||
@@ -279,8 +280,8 @@ export function sweepStaleReviews(
|
||||
detail: "Auto-approved by review timeout escalation",
|
||||
actor: "system",
|
||||
});
|
||||
} catch {
|
||||
// Already decided (race condition with manual approval) -- skip silently (Pitfall 3)
|
||||
} catch (err) {
|
||||
log.warn(`[task-quality-gates] resolveApprovalDecision failed for approval ${pendingApproval.id} (likely already decided):`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,8 +289,8 @@ export function sweepStaleReviews(
|
||||
try {
|
||||
deps.db.advanceTaskStatus(task.id, "done", "Auto-advanced: review timeout exceeded 30 minutes.");
|
||||
deps.handleTaskStatusChange(task.id, companyId, "in_review", "done");
|
||||
} catch {
|
||||
// Task may have been updated by manual action
|
||||
} catch (err) {
|
||||
log.warn(`[task-quality-gates] advanceTaskStatus failed for task ${task.id}:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
|
||||
escalatedCount++;
|
||||
|
||||
@@ -139,7 +139,7 @@ export function createThreadIntegration({ db, logger, emitEvent, publishDomainCh
|
||||
});
|
||||
notifyThreadSubscribers(db, thread.id, thread.title, approvalReason, notify);
|
||||
}
|
||||
announceApprovalToBoard(db, approval, { emitEvent, publishDomainChanged });
|
||||
announceApprovalToBoard(db, approval, { emitEvent, publishDomainChanged, logger });
|
||||
} catch (err) {
|
||||
logger.error(`[thread-integration] onApprovalCreated failed: ${err instanceof Error ? err.message : String(err)}`);
|
||||
}
|
||||
@@ -305,7 +305,8 @@ export function createThreadIntegration({ db, logger, emitEvent, publishDomainCh
|
||||
try {
|
||||
const task = db.getTask(thread.relatedEntityId);
|
||||
if (task.assigneeAgentId !== toAgentId) continue;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[thread-integration] getTask failed for thread ${ws.threadId}:`, err instanceof Error ? err.message : String(err));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -321,7 +322,8 @@ export function createThreadIntegration({ db, logger, emitEvent, publishDomainCh
|
||||
try {
|
||||
const waitStates = db.listThreadWaitStates(companyId);
|
||||
return waitStates.length > 0;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[thread-integration] hasUnresolvedWaitStates query failed for company ${companyId}:`, err instanceof Error ? err.message : String(err));
|
||||
return false;
|
||||
}
|
||||
},
|
||||
@@ -343,7 +345,8 @@ export function createThreadIntegration({ db, logger, emitEvent, publishDomainCh
|
||||
companyId, agentId,
|
||||
);
|
||||
return rows.length > 0;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[thread-integration] hasUnresolvedWaitStatesForAgent query failed (company=${companyId} agent=${agentId}):`, err instanceof Error ? err.message : String(err));
|
||||
return false;
|
||||
}
|
||||
},
|
||||
@@ -355,7 +358,8 @@ export function createThreadIntegration({ db, logger, emitEvent, publishDomainCh
|
||||
countUnresolvedWaitStates(companyId: string): number {
|
||||
try {
|
||||
return db.listThreadWaitStates(companyId).length;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
logger.warn(`[thread-integration] countUnresolvedWaitStates failed for company ${companyId}:`, err instanceof Error ? err.message : String(err));
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { ThreadType } from "@shared/types";
|
||||
|
||||
export interface ThreadIntegrationDeps {
|
||||
db: AppDatabase;
|
||||
logger: { info: (...args: unknown[]) => void; error: (...args: unknown[]) => void };
|
||||
logger: { info: (...args: unknown[]) => void; warn: (...args: unknown[]) => void; error: (...args: unknown[]) => void };
|
||||
emitEvent?: (event: import("@shared/types").DesktopEvent) => void;
|
||||
publishDomainChanged?: () => void;
|
||||
notify?: (options: { title: string; body: string }) => void;
|
||||
@@ -64,6 +64,7 @@ function resolveApprovalAnnouncementChannel(
|
||||
companyId: string,
|
||||
taskId: string | null,
|
||||
requestedByAgentId: string | null,
|
||||
logger: ThreadIntegrationDeps["logger"],
|
||||
): { channel: "department" | "project" | "company"; channelTargetId: string | null } | null {
|
||||
if (requestedByAgentId) {
|
||||
try {
|
||||
@@ -71,8 +72,8 @@ function resolveApprovalAnnouncementChannel(
|
||||
if (requester.companyId === companyId && requester.department) {
|
||||
return { channel: "department", channelTargetId: requester.department };
|
||||
}
|
||||
} catch {
|
||||
// Fall through to task/project/company targeting.
|
||||
} catch (err) {
|
||||
logger.warn(`[thread-helpers] getAgent failed for requester ${requestedByAgentId}; falling through to task targeting:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,8 +83,8 @@ function resolveApprovalAnnouncementChannel(
|
||||
if (task.companyId === companyId && task.projectId) {
|
||||
return { channel: "project", channelTargetId: task.projectId };
|
||||
}
|
||||
} catch {
|
||||
// Fall through to company-wide routing.
|
||||
} catch (err) {
|
||||
logger.warn(`[thread-helpers] getTask failed for task ${taskId}; falling through to company routing:`, err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +101,7 @@ export function announceApprovalToBoard(
|
||||
payloadSummary: string;
|
||||
impactSummary: string;
|
||||
},
|
||||
deps: Pick<ThreadIntegrationDeps, "emitEvent" | "publishDomainChanged">,
|
||||
deps: Pick<ThreadIntegrationDeps, "emitEvent" | "publishDomainChanged" | "logger">,
|
||||
): void {
|
||||
if (!approval.requestedByAgentId || !deps.emitEvent || !deps.publishDomainChanged) return;
|
||||
|
||||
@@ -109,6 +110,7 @@ export function announceApprovalToBoard(
|
||||
approval.companyId,
|
||||
approval.relatedTaskId,
|
||||
approval.requestedByAgentId,
|
||||
deps.logger,
|
||||
);
|
||||
if (!target) return;
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ describe("company mode switching", () => {
|
||||
|
||||
const threadIntegration = createThreadIntegration({
|
||||
db,
|
||||
logger: { info: () => {}, error: () => {} },
|
||||
logger: { info: () => {}, warn: () => {}, error: () => {} },
|
||||
});
|
||||
|
||||
threadIntegration.onTaskCreated(taskId, companyId);
|
||||
|
||||
@@ -77,6 +77,7 @@ describe("ipc-core-agent-graph-handlers model enforcement", () => {
|
||||
findInvalidReference: () => null,
|
||||
publishDomainChanged: vi.fn(),
|
||||
handleApprovalResolved: vi.fn(),
|
||||
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
||||
});
|
||||
|
||||
const saveAgent = handlers.get(IPC_CHANNELS.saveAgent);
|
||||
|
||||
@@ -174,7 +174,7 @@ describe("message dispatch", () => {
|
||||
const { companyId, ctoId, taskId } = createScaffold(db);
|
||||
const threadIntegration = createThreadIntegration({
|
||||
db,
|
||||
logger: { info: () => {}, error: () => {} },
|
||||
logger: { info: () => {}, warn: () => {}, error: () => {} },
|
||||
});
|
||||
|
||||
threadIntegration.onTaskCreated(taskId, companyId);
|
||||
|
||||
Reference in new issue
Block a user