Commit Graph
29 Commits
Author SHA1 Message Date
ZacharyZhang-NY 118e3d8795 Remove final AppDatabase pass-through methods with ESLint guard (C1 part 3/3)
Migrate all remaining pass-through call sites (social, skills, budget,
entityCrud, documents, knowledge, automation, wakeup, backup, threads,
settings, snapshot, connectors, workspaces, activity) to their owning
domain modules. `AppDatabase` now carries only three methods:

  - createHireRequest (multi-module composite: agents + approvalsMod)
  - rollbackConfigRevision (multi-module composite: documents + entityCrud)
  - queryAll (raw-SQL escape hatch, @internal)

The vault/env-binding infrastructure previously passed argument-by-
argument into `entityCrud.saveSecret`/`resolveSecretEnv` now binds at
module construction, letting callers invoke `db.entityCrud.saveSecret({...})`
without carrying vault plumbing.

Structural types that narrowed AppDatabase to a bag of flat methods
(`ConnectorModelPolicyDeps` callers, `CapabilityMatchDb`, test mocks in
budget-enforcement / budget-incidents / task-quality-gates /
failure-escalation / task-quality-gates-sweep) are reshaped to the new
namespace. `connectorPolicyDepsFromDb` replaces the old pattern of
handing the whole AppDatabase to `validateConnectorModelSelection`.

A new ESLint rule in eslint.config.mjs blocks new top-level methods on
AppDatabase so the pass-through layer cannot silently grow back.
`AppDatabase.ts` dropped from 193 lines to 58; `AppDatabaseBase` lost
the 270-line trailing pass-through block.
2026-04-20 13:31:39 -04:00
ZacharyZhang-NY a69fbb053b Codemod db pass-through calls: messages + runs + approvals + company (C1 part 2/3)
Rewrite ~376 call sites for messages, runs, approvalsMod, and company
domains and delete the corresponding pass-through methods on
AppDatabase / AppDatabaseBase. Structural type references
(ReturnType<HandlerDeps["db"]["getRun"]>) and SweepStaleReviewsDeps-style
mocks are updated to route through the owning sub-module.
2026-04-20 13:20:15 -04:00
ZacharyZhang-NY 5e9b7acbc6 Codemod db pass-through calls to domain modules: tasks + agents (C1 part 1/3)
Rewrite ~525 call sites from `db.<method>()` to `db.<module>.<method>()`
for tasks and agents domains (including `getAgent`/`findAgent`/`getTask`
which live on the `entityCrud` field). Delete the corresponding
pass-through methods on AppDatabaseBase. Structural types that narrowed
`db` to a bag of flat methods (CapabilityMatchDb, EscalationDeps,
SweepStaleReviewsDeps) are reshaped to match the new namespace. Internal
callback references inside database.ts/database-base.ts now invoke the
owning module directly (e.g. `this.agents.saveAgent`) rather than the
deleted pass-through.

The codemod lives at scripts/codemod/remove-appdatabase-passthrough.mjs
and filters by owning module + optional explicit method list. It is
idempotent and does not re-rewrite already-namespaced calls.
2026-04-20 13:17:01 -04:00
ZacharyZhang-NY 76d9031a41 Reorganize worker entry layer into worker/entry/ (M8 part 1/3)
Pulls the worker process entry point, parent<->worker IPC plumbing, message validation schema, and crash-recovery handler into a single src/main/worker/entry/ module. No runtime behavior changes — only directory + file-name renames plus import-path updates: worker-entry.ts -> entry/index.ts, worker-message-handler.ts -> entry/message-handler.ts, worker-rpc.ts -> entry/rpc.ts, worker-crash-recovery.ts -> entry/crash-recovery.ts, worker-types.ts -> entry/types.ts. electron.vite.config.ts, worker-service.ts, handlers/*.ts, pi-agent-factory.ts, and worker-runner.ts updated to point at the new locations.
2026-04-20 11:38:46 -04:00
ZacharyZhang-NY e917c614c9 Gate inbox delivery on worker-side steer ACK
Replace the fire-and-forget steer with a correlated ACK round-trip so main
only marks an inbox row delivered once the worker confirms the message
landed on the live Agent. Steer against a run that is still in the
`preparing` window or has already started `closing` now falls back to the
wake + inbox projection path instead of being silently dropped.

Worker publishes `run-agent-state` transitions at Agent construction and
teardown; main tracks these on `ActiveRunContext.state` and refuses to
steer runs that are not `agent_active`. A rejected or timed-out ACK
triggers `wakeAgentIfPossible` so the next `prepareRun` replays the row.
2026-04-19 14:38:04 -04:00
ZacharyZhang-NY e0e43d6127 refactor(session): nuke dead sessionParams / sessionDisplayId pipeline (v43)
These columns and the messages that plumbed them existed for the CLI-
adapter era — Gemini's resume id and provider-specific session params
rode the run-finished envelope back onto the session row so the next
turn could resume. Pi Mono dispatches every provider through pi-ai and
emits neither value. The worker has been posting literal null on every
finish, finalizeRun has been writing null into task_sessions, and
rotateSession has been carrying null forward.

Remove the wire, the writer, the carry-forward, and the columns:

  - v43 migration rebuilds task_sessions without session_params_json /
    session_display_id, and runs without session_display_id. Follows
    the idempotent "create new, copy, swap, reindex" pattern used by
    v41. context_snapshot_json is preserved (also unreferenced today
    but outside this cleanup's scope).
  - RunJob / EnqueueRunPayload / ChildMessage / worker-types zod
    schema no longer carry the fields. finishRun() / createNewSession()
    signatures tighten in lockstep.
  - rotateSession starts the new session with fresh counters and no
    carry-forward.
  - turn-context resolveSession returns just the session id.
  - TaskSessionRecord / RunRecord lose the dead fields.

498/498 unit tests pass. typecheck + build clean.
2026-04-18 23:22:17 -04:00
ZacharyZhang-NY 7e1d4199ee refactor(schema): make runs.task_id nullable, delete heartbeat workaround
The NOT NULL constraint forced heartbeat runs to synthesize a fake task
row (`[Heartbeat] X — Check for work`) so the FK could resolve. Three
layers of code — acquireHeartbeatTask, findReusableHeartbeatTask,
cleanupDuplicateHeartbeatTasks, plus the is_heartbeat flag column + UI
filter — existed to paper over one bad constraint. Remove the
constraint and the whole workaround vanishes.

SQLite table rebuild with data preservation. Live production DB, no
backup — migration is atomic, idempotent, and verified before DROP.

## Migration v35 (rebuild runs with nullable task_id)

Uses `fn:` form so it can:
- Snapshot `COUNT(*) FROM runs` before copy
- Walk `PRAGMA table_info(runs)` to detect drift columns (v34 declared
  a column set; any additive ALTER since v14 shows up here)
- Assert `COUNT(*) FROM runs_new == beforeCount` before `DROP TABLE runs`
- `PRAGMA defer_foreign_keys = ON` so run_events(run_id) FK defers to
  commit time, by which runs has the same id set under the new name
- Recreate all 6 indexes on the new table

If the assertion fails, the migration throws inside the existing
`transaction()` wrapper. SQLite rolls back the whole migration, old
`runs` table is untouched, `schema_migrations` never records v35. Next
app start retries cleanly.

## Migration v36 (drop tasks.is_heartbeat + synthetic rows)

Runs now carry `task_id = NULL` for heartbeat dispatches. The
`is_heartbeat` flag is dead data going forward.

1. `UPDATE runs SET task_id = NULL WHERE task_id IN (SELECT id FROM tasks WHERE is_heartbeat = 1)`
2. `DELETE FROM tasks WHERE is_heartbeat = 1` (synthetic rows, not real work)
3. Rebuild `tasks` without the `is_heartbeat` column (same defer-FK +
   row-count-assert pattern as v35)

## Code cleanup

- `RunRecord.taskId: string | null`; `createRun` / `cancelRunForDeletion`
  / `finishRun` guard every `UPDATE tasks` path with a null-check.
- `TaskRecord.isHeartbeat` removed; every SELECT / filter / UI reference
  dropped.
- Delete `acquireHeartbeatTask` / `findReusableHeartbeatTask` /
  `cleanupDuplicateHeartbeatTasks` — dead after the constraint lifts.
- Delete `src/main/orchestration/heartbeat-task.ts` (pure workaround
  constant file).
- `heartbeat.ts` creates runs with `taskId: null` when the agent has no
  assigned work. Turn context already supports nullable task
  (heartbeatFallbackSection); prompt surface unchanged.
- `validateTaskCreation` drops the `isHeartbeat` parameter — user-
  authored + automation-authored tasks are never heartbeat.
- Renderer: `run.taskId ? taskMap.get(...) : undefined` in Dashboard /
  RunCenter / RunDetail / RunQueue.
- Delete `tests/unit/heartbeat-coalescing.test.ts` (12 tests that
  exercised the deleted DB methods).
- Drop 1 isHeartbeat-bypass test from `task-quality-gates.test.ts`.

## Grep verification

- `isHeartbeatTaskTitle src/` → 0
- `acquireHeartbeatTask src/` → migration comment only
- `is_heartbeat src/` → schema.ts migration history only
- `isHeartbeat src/` → 0

Tests: 578/578 (was 591 — delta is 13 tests that tested deleted code).
2026-04-17 16:16:08 -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 4de9cd0339 refactor(worker): use pi-mono Agent directly
Rewrites the utility-process worker to drive pi-agent-core's Agent class
against pi-ai models and pi-coding-agent tools. The entry file keeps the
queue and cancellation state, delegates runs to WorkerRunner, and routes
RPC responses through WorkerRpcClient so beforeToolCall/afterToolCall
approvals and prepareRun credential resolution travel back to main's
RuntimeBridge. Worker message schema gains `agent-event` + `rpc-request`
envelopes, and run-finished no longer depends on the deleted sessionCodec.
2026-04-17 02:21:12 -04:00
ZacharyZhang-NY 169f5d686c fix(worker-service): exponential backoff + halt after MAX_CRASH_LOOP respawns 2026-04-16 23:09:40 -04:00
ZacharyZhang-NY 2286d70758 fix(worker-service): cancel kill-timer on worker exit; log shutdown failure 2026-04-16 23:08:37 -04:00
ZacharyZhang-NY a1f021ff4a fix(worker): invert connector bypass default and gate on packaged build (S-01)
Previously `AGENTCOMPANY_ALLOW_UNSAFE_CONNECTOR_BYPASS !== "false"` meant
packaged builds defaulted to passing `dangerouslyBypassApprovalsAndSandbox`,
`dangerouslySkipPermissions`, `yolo`, and `--sandbox danger-full-access`
to CLI connectors, bypassing ApprovalService entirely.

Require both explicit opt-in (`AGENTCOMPANY_ALLOW_UNSAFE_CONNECTOR_BYPASS=true`)
and a non-packaged build. Packaged builds hard-refuse regardless of the env
var. Main process forks the worker with `AGENTCOMPANY_PACKAGED=<app.isPackaged>`
so the worker has an authoritative packaged signal.
2026-04-16 21:48:10 -04:00
ZacharyZhang-NY 67c757f481 update 2026-04-04 12:03:50 -04:00
ZacharyZhang-NYandClaude Sonnet 4.6 66158e222e update
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 22:02:28 -04:00
ZacharyZhang-NY 65e85135c1 large update 2026-03-29 14:08:15 -04:00
ZacharyZhang-NYandClaude Opus 4.6 ffab9888ed chore(09-02): merge main to get plan 01 infrastructure
- Resolve merge conflicts in worker-service.ts and ipc-core-work-graph-handlers.ts
- Keep granular emitEvent calls from plan 02 while preserving session management and task validation from main
- Fix duplicate createSkillService import in service-graph.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:47:33 -04:00
ZacharyZhang-NYandClaude Opus 4.6 5124209ddb feat(09-02): convert high-frequency callsites to granular emitEvent
- Replace publishDomainChanged() with granular events at 9 high-frequency callsites
- saveTask/deleteTask emit task-updated events in ipc-core-work-graph-handlers
- decideApproval emits approval-updated, addComment emits task-updated
- worker-service emits run-updated for status changes and completion
- worker-service emits agent-updated for budget hard-stop pause
- orchestration-service emits agent-updated for auto-pause, run-updated for dispatch
- Wire emitEvent dependency to orchestration-service via service-graph
- Keep publishDomainChanged for low-frequency and multi-entity paths

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:45:30 -04:00
ZacharyZhang-NYandClaude Opus 4.6 5271f7ee5d feat(06-02): wire token usage tracking, session accumulation, and rotation into worker pipeline
- Add inputTokens/outputTokens fields to worker run-finished messages and workerMessageSchema
- Wire sessionService into WorkerServiceDependencies and ServiceGraph lazy slot
- Add token accumulation and rotation evaluation in run-finished handler
- Add 7 wiring integration tests verifying accumulation, rotation, and guard paths

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 23:03:23 -04:00
ZacharyZhang-NY 4e5429929a feat(05-02): replace flat budget check with policy-based gate, wire incident checks into run completion and heartbeat
- Replace flat budgetMonthlyUsd check with evaluateBudgetGate in getRunEligibilityFailure
- Add projectId parameter to getRunEligibilityFailure for project-scope policy evaluation
- Update all 3 call sites to pass projectId where task context is available
- Wire checkAndCreateIncidents into worker-service run-finished handler for post-run incident creation
- Wire autoCloseExpiredIncidents into heartbeat for window rollover cleanup (D-06)
- Pass budgetService dependency to orchestration-service and worker-service via service-graph
- Mark autoPauseBudgetExceededAgent as deprecated (superseded by BudgetEnforcementService)
2026-03-26 21:02:12 -04:00
ZacharyZhang-NY 930273ce87 feat(04-02): worker lock release, wake promotion, IPC wakeup channel, and preload bridge
- Add releaseTaskLock to worker crash recovery after finishRun (EXEC-02)
- Track affected agent company IDs for targeted wake promotion
- Replace listWakeupRequests+loop with promoteWakeupRequests+wakeAgentIfPossible in run completion
- Track hadDeferredWakes flag for circuit breaker self-wake decision
- Add listWakeupRequests IPC channel to IPC_CHANNELS
- Add wakeupQuerySchema with Zod validation and WakeupRequestRecord interface
- Add listWakeupRequests to DesktopApi type
- Add listWakeupRequests preload bridge method
- Add registerHandle for listWakeupRequests IPC handler with company validation
2026-03-26 19:16:17 -04:00
ZacharyZhang-NY 4d47712669 fix(04-01): resolve type errors and update integration tests for wakeup API
- Fix SQLInputValue type in listWakeupRequests params
- Fix leftover pendingWakes reference in worker-service.ts
- Fix RunStatus 'completed' -> 'succeeded' in atomic-checkout test
- Update autonomous-chain.spec.ts to use new wakeup lifecycle API
2026-03-26 19:00:51 -04:00
ZacharyZhang-NY 08935b3379 feat(04-01): implement CAS checkout, lock release, stale recovery, and migration 16
- Rewrite checkoutTask to use runId-based CAS with checkout_run_id IS NULL guard
- Add releaseTaskLock and recoverStaleLocks methods
- Add lock release in finishRun and cancelRunForDeletion exit paths
- Add migration 16: pending_wakes to wakeup_requests cutover with DROP TABLE
- Replace all pending_wakes references with wakeup_requests
- Add wakeup lifecycle CRUD: enqueue, claim, complete, defer, promote, list
- Update api-server, orchestration-service, worker-service callers
- Update existing database.test.ts for new checkoutTask signature
2026-03-26 18:56:54 -04:00
ZacharyZhang-NYandClaude Opus 4.6 05c57be98f fix: prevent agents from running indefinitely after app quit
Agents kept running after closing AgentCompany because worker processes
auto-recreated on exit, retry timers were never cleared, child CLI
processes weren't terminated, and orchestration had no quit guards.

Changes:
- Add isQuitting guards to all orchestration/automation entry points
- Track and clear all setTimeout/setInterval timers on quit
- Send cancel-run to all active CLI processes before killing worker
- Add destroyed flag to prevent worker recreation after intentional destroy
- Add quit confirmation dialog: Stop All & Quit / Minimize to Background / Cancel
- Bump version to 0.4.96

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 12:40:58 -04:00
ZacharyZhang-NYandClaude Opus 4.6 d52985f61e feat: deliverable review gate, app lifecycle fixes, CI/CD pipeline
- Add deliverable review gate: pause workflow when agents produce important
  documents (PRDs, design docs, etc.) for human review before continuing
- Add company-level reviewDeliverables setting with onboarding card selector
- Block agent heartbeats when deliverable review is pending (agent + CEO)
- Trigger review at document creation time in API server for reliability
- Fix autoApproveHires vs reviewDeliverables conflict in approval-policy
- Fix app quit: force-exit on all platforms (macOS Dock, Windows tray, Linux)
- Add multi-platform CI/CD pipeline (macOS arm64/x64, Windows x64/arm64,
  Linux x64/arm64 with deb/AppImage/rpm/snap) with code signing support

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 23:31:48 -04:00
ZacharyZhang-NYandClaude Opus 4.6 667ac48aa5 feat(04-01): refactor all notify() call sites to use structured NotificationOptions
- Change ipc-support.ts notify() from (title, body) to (options: NotificationOptions)
- Wire createNotificationService in bootstrap-application.ts with lazy service graph refs
- Update all 12 call sites with entity context, urgency, navigation, and batch keys
- Update dependency interfaces in service-graph, worker, orchestration, automation, updater, desktop-shell
- No string-based notify() calls remain in src/main/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 16:37:07 -04:00
ZacharyZhang-NY f3874568a2 feat(02-03): retry-aware failure escalation pipeline
- Add MAX_RETRY_ATTEMPTS (2) and RETRY_COOLDOWN_MS (30s) constants
- Replace immediate escalation with retry-then-escalate logic
- Escalation chain: retry same agent -> manager -> CEO -> user notification
- Reset retry count on successful task completion
- Update Reassign on run failure automation rule with afterRetryExhaustion condition
- Add comprehensive unit tests for escalation state machine (11 tests)
2026-03-22 03:53:18 -04:00
ZacharyZhang-NY 48daca9ba0 update 2026-03-18 16:13:17 -04:00
ZacharyZhang-NY 1ba3147ed2 update 2026-03-18 01:15:58 -04:00