Commit Graph
20 Commits
Author SHA1 Message Date
ZacharyZhang-NY 40040b3dd2 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.
2026-04-17 15:25:13 -04:00
ZacharyZhang-NY 14dc9a4897 refactor(cost): single writer for cost_entries, finishRun reads SUM
Cost was written through three independent paths: worker-runner shipped
`finalUsage.costUsd` in run-finished -> finishRun wrote `runs.cost_usd`,
`tasks.cost_usd += costUsd`, `agents.spent_monthly_usd += costUsd`, and
inserted a per-run aggregate row in cost_entries. CostWriter
additionally inserted per-message rows in cost_entries from main. Any
CostWriter failure created silent drift between `SUM(cost_entries)`
(read by budget enforcement) and `runs.cost_usd` (read by the UI).

Make CostWriter the single writer, finishRun the single reader.

- Add `db.recordCostEntry(...)` as the first-class insert path for
  per-message cost rows. CostWriter calls it; tests call it directly.
- Remove the aggregate INSERT from finishRun. finishRun now reads
  `SELECT COALESCE(SUM(amount_usd), 0) FROM cost_entries WHERE run_id=?
  AND unattributed=0` and writes that as the authoritative run total to
  runs.cost_usd, tasks.cost_usd, and agents.spent_monthly_usd.
- Drop `costUsd` from `finishRun` input, the worker `run-finished`
  ChildMessage shape, the automation `RunFinishedEvent` type, and every
  caller + test fixture. Cost is owned by the per-message path.
- worker-runner stops extracting `usage.cost.total` — token counts stay
  for session accumulation, cost comes from pi-ai via CostWriter.
- run-finished-helpers now gates budget incident checks on
  `event.status === "succeeded"` instead of a cost field that no longer
  exists on the event.
- automation-run-completion fires `budget_threshold` on every succeeded
  run (post-run spent_monthly_usd already reflects the SUM path).
- Update budget tests to write a `recordCostEntry` before `finishRun`
  to simulate the per-message cost path; drop `costUsd` from cross-
  subsystem, edge-case-budget, database, and related fixtures.

One writer, one reader, one source of truth. No drift.
2026-04-17 15:04:22 -04:00
ZacharyZhang-NY 5547c29491 update 2026-04-17 14:03:56 -04:00
ZacharyZhang-NY a78bdb9b02 refactor(automation): type DEPARTMENT_SKILL_KEYWORDS with DepartmentId enum
Replaces Record<string, string[]> with Record<DepartmentId, string[]>
so future edits to the department taxonomy trigger TS errors for any
keyword map that drifts. Cast at the lookup site reflects the fact
that agent.department is a free-form string column that may hold
legacy or custom values.
2026-04-17 01:10:49 -04:00
ZacharyZhang-NY e892bf3366 refactor(main): simplify escalation trigger map + unify api-section curl guidance
- P3-52: replace 5-deep if-chain in deriveEscalationWakeTrigger with two
  lookup maps (status takes precedence over rule trigger), which removes
  the cognitive overhead of reading the chain and makes the two inputs'
  roles explicit.
- P3-53: rewrite the Windows-only curl warning as platform-neutral
  guidance that matches the surrounding examples. The old text said
  'prefer fetch on Windows' while the doc showed curl POSTs everywhere;
  the new text recommends fetch universally and explains when the curl
  temp-file pattern is needed.
2026-04-16 23:27:29 -04:00
ZacharyZhang-NY 32e676aec9 fix(automation): allow explicit scheduledAt / scheduleOffsetMinutes
P3-58: Automation rule's schedule_meeting action hardcoded +1hr from
now, producing meetings that could land in the past or outside working
hours. Accept scheduledAt (ISO-8601) first, fall back to
scheduleOffsetMinutes, and only default to +60min when the rule
specifies no time at all.
2026-04-16 23:06:06 -04:00
ZacharyZhang-NY d4c0d0a6bc refactor(db): move run-deliverable detection out of raw db.queryAll
P2-37: automation-run-completion.checkRunProducedDeliverable issued 5
raw SQLs via db.queryAll. Consolidate into db.runProducedDeliverable
so schema changes to knowledge_base / documents / task_comments
surface in the knowledge domain module rather than silently breaking
at the caller.
2026-04-16 23:02:43 -04:00
ZacharyZhang-NY 5403ee9aa0 fix(approval-handler): only parse workflow payload for dangerous_command type
P1-22: The opportunistic JSON.parse of payloadJson ran for every
approval, hoping to find workflowId/stepId. Gate on the actual
approval type that carries workflow metadata (dangerous_command, as
emitted by runWorkflowPipeline), and validate payload shape via zod.
2026-04-16 23:01:23 -04:00
ZacharyZhang-NY 7df0d564e5 refactor(workflow): validate workflow steps via zod schema
P1-20: normalizeWorkflowSteps used ad-hoc typeof checks that silently
accepted malformed entries (e.g. number where string was expected).
Replace with workflowStepsInputSchema zod validation; semantics for
defaults/action whitelist are preserved.
2026-04-16 23:00:45 -04:00
ZacharyZhang-NY 2df921ca88 refactor(agents): share leadership role detector, use word-boundary match
P2-23: 'lead' in system-prompt.ts matched 'cleaner' / 'leaderboard' via
substring .includes(). Hoist LEADERSHIP_ROLE_KEYWORDS into a shared
agent-role-helpers.ts module; tokenize input on non-alphanum to require
whole-word match.

P3-54: Same keyword list was duplicated in automation-skills with drift
(missing 'ceo', different members). Both sites now call through the
shared helper.
2026-04-16 22:57:35 -04:00
ZacharyZhang-NY 2deaa0d8b8 feat(automation): add per-action zod schemas for config validation
P2-29: JSON.parse(actionConfigJson) with blanket '(config.x as string)'
coerced malformed config to placeholder values silently (e.g. numeric
priority landed as 'medium'). Introduce parseActionConfig(action, json)
backed by per-action zod schemas in automation-action-schemas.ts.
Validation failures throw, surfaced by evaluateAutomationRules' logger
+ execution recorder. Individual case branches keep existing default
semantics for optional fields.
2026-04-16 22:56:18 -04:00
ZacharyZhang-NY bb397dff02 refactor(heartbeat): centralize [Heartbeat] prefix as a shared constant
P2-36: Multiple subsystems were string-coupled via the magic
'[Heartbeat]' prefix. Introduce src/main/orchestration/heartbeat-task.ts
as the single source of truth and replace scattered literals in
heartbeat dispatch, automation actions, and run completion.

P3-52: Collapse 5-deep ternary in automation-actions' wake-trigger
selection into lookup helpers (deriveHeartbeatWakeTrigger,
deriveEscalationWakeTrigger).
2026-04-16 22:53:19 -04:00
ZacharyZhang-NY 66f5e0923d fix(automation): log no-op status-change calls instead of silently returning
P2-34: Silent no-op guard hid caller bugs. Warn-log the violation so
it shows up in traces; callers should still gate.
2026-04-16 22:51:12 -04:00
ZacharyZhang-NY 4ebd98bb99 fix(automation): surface malformed condition JSON instead of silently returning false
P2-46: Previous bare try/catch hid parse errors, making rules silently
non-firing. Let JSON.parse throw; the caller (evaluateAutomationRules)
already logs and records the failure.
2026-04-16 22:51:00 -04:00
ZacharyZhang-NY 62050c9c05 refactor(automation): use JSON-stringified execution keys
P1-19: Previous string-join with ':' separator could collide when
field values contain colons (e.g. runId='a:b' vs runId='a' + previousStatus='b').
Use JSON.stringify for unambiguous serialization.
2026-04-16 22:50:38 -04:00
ZacharyZhang-NY 1a57622849 fix(automation): remove silent JSON.parse fallback in executeAutomationAction 2026-04-16 21:36:41 -04:00
ZacharyZhang-NY 9779c4d1ca update 2026-04-07 20:18:47 -04:00
ZacharyZhang-NY f5d4f8b705 update 2026-04-06 00:35:27 -04:00
ZacharyZhang-NY 9145572422 update 2026-04-05 20:14:48 -04:00
ZacharyZhang-NY 67c757f481 update 2026-04-04 12:03:50 -04:00