Split the single 15s setInterval in scheduler.ts (6 serial jobs) into 4
independent schedulers so a slow job no longer stalls the others:
- scheduler-heartbeat.ts (15s): wake eligible agents, CEO bootstrap,
schedule automation trigger
- scheduler-lock-recovery.ts (60s): stale task-lock recovery and stale
workspace-lock release
- scheduler-idle-automation.ts (60s): idle-agent automation dispatch
- scheduler-housekeeping.ts (300s): monthly budget reset, expired budget
incident auto-close, stale review sweep, expired wakeup purge
scheduler.ts is now a thin composer exposing startAllSchedulers /
stopAllSchedulers. Bootstrap and service-graph updated accordingly.
Business logic, error isolation, and atomic actions preserved.
Pi-ai's `streamSimple`/`SimpleStreamOptions` has no per-request env
escape hatch; its Azure/AWS/GCP ADC providers read `process.env`
directly. The previous implementation wrote `extraEnv` into
`process.env` globally and relied on a `finally` after
`stream.result()` to clean up, so two concurrent `testCredential`
calls would clobber each other's env, expose the wrong credential
to in-flight streams, and on error paths fail to restore
previously-set values.
Wrap the env-mutating branch in a module-level promise chain so at
most one `extraEnv` test runs at a time, snapshot the prior value
of every key we overwrite, and restore (or delete when unset) in
`finally`. Tests that resolve to `extraEnv: undefined` bypass the
mutex entirely.
showMessageBoxSync froze the main-process event loop while the user
decided whether to open an external link, stalling agent-event workers,
IPC, and the scheduler for the duration of the dialog. setWindowOpenHandler
still returns synchronously with action: "deny"; the confirmation dialog
runs in an async continuation that only reaches shell.openExternal when
the user approves.
Origin-less requests previously skipped the allowlist entirely, meaning
any process that omitted the Origin header (trivial for a non-browser
attacker) could reach the auth layer. Browsers always send Origin, so
rejecting Origin-less browser-style requests closes the bypass without
breaking agent CLIs, which authenticate with a bearer token instead.
New policy:
- Requests with a Bearer Authorization header: Origin optional; if
present it must be in the allowlist.
- All other requests (including CORS preflight): Origin required and
must be in the allowlist.
Every handler was wrapping its body in try/catch and returning fail(code,
error.message), leaking raw DB messages to the renderer and bypassing the
sanitizer in registerHandle. Removed the 14 inner try/catch shells so
errors surface through ipc-support's IPC_HANDLER_FAILED / IPC_INVALID_PAYLOAD
path. Also removed the duplicate emitEvent({ type: 'domain-changed' }) in
addThreadEvent — publishDomainChanged already emits the same event.
safeGetAgent was catching all errors and returning null, causing every
permission check (canControlAgent / canManageOrganization / canRequestHire)
to silently deny when the database threw. Only swallow the 'Agent not found'
case and let real DB errors propagate up to registerHandle's sanitizer.
- Remove self-defeating heartbeat task filter that excluded heartbeat tasks from candidates
- Add Paperclip-style task reuse: recycle existing done/in_review heartbeat tasks instead of creating new ones
- Fix status bug: new heartbeat tasks created as "todo" instead of "in_progress" so CAS checkout succeeds
- Wire one-time duplicate cleanup sweep into heartbeat scheduler startup
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add findReusableHeartbeatTask() to find most recent done/in_review heartbeat task per agent
- Add cleanupDuplicateHeartbeatTasks() to cancel old duplicates using ROW_NUMBER window function
- Add 12 unit tests covering coalescing behavior, cross-agent isolation, status filtering
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Step 1: review available skills before designing org structure
- Step 3: assign skills to agents after each hire is approved
- Priority 3: check/assign agent skills before delegating tasks
- GET /api/companies/:companyId/skills (list company skills)
- GET /api/skills/:skillId (skill detail with markdown)
- POST /api/agents/:agentId/skills (assign skill to agent)
- DELETE /api/agents/:agentId/skills/:skillId (remove skill)
- GET /api/agents/:agentId/skills (list agent's skills)
- Kickoff task now instructs CEO to explore workspace codebase before planning
- Company name and description interpolated into both activeGoal and fallback branches
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Skills from user home directories are now auto-imported when listing
company skills. Deduplication handled by canonical key uniqueness.
Scans: ~/.agents/skills/, ~/.claude/skills/, and workspace .agents/skills/.
Cross-platform: uses HOME or USERPROFILE env var.
- ChannelSidebar: replace inline color-mix style with Tailwind classes for task discussion icons
- MessageItem: replace broken priority config (color-mix inline) with Tailwind utility classes
- MessageItem: replace broken department badge hex-alpha pattern with Tailwind classes
- automation-config: restore ACCENT/LINE as hex values (used in template literal alpha suffixes)
- desktop-shell-service: set nativeTheme.themeSource to sync Windows title bar with app theme
Add scanWorkspaceSkills method to skill service that iterates all
workspace local paths for a company and imports SKILL.md files from
.agents/skills/ subdirectories. Uses path.join for cross-platform
compatibility (Windows/macOS/Linux). The scan runs automatically
when skills are listed via IPC, with graceful error handling per
workspace so one bad path does not block the entire listing.
Also adds listWorkspaceLocalPaths to database for retrieving workspace
paths by company.
Change BrowserWindow backgroundColor from dark #13110f (leftover from
old design tokens) to #ffffff so the title bar area matches light mode.
The old dark color was visible in light mode as a dark title bar strip.
- 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>
- 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>
- Import sweepStaleReviews, resolveApprovalDecision, dispatchAgentMessage
- Insert stale review sweep into per-company heartbeat loop after budget reset
- Sweep runs on every 15-second heartbeat cycle for each active company
- Local callback stubs for handleTaskStatusChange and handleApprovalResolved
- Error handling prevents sweep failures from crashing the heartbeat loop
- Fix test type casts to use as-unknown-as for vi.fn mock compatibility
- TypeScript compiles cleanly, 249/249 tests pass
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- sweepStaleReviews queries tasks in in_review status exceeding 30-min timeout
- Escalation dispatched to reviewer's manager (manager's manager in chain)
- When reviewer is CEO (no higher manager), desktop notification sent to human
- Pending approvals auto-approved with documented timeout note
- Task status advanced from in_review to done after escalation
- Race condition with manual approval handled via try-catch (Pitfall 3)
- Tasks without assigneeAgentId skipped gracefully
- Exports REVIEW_TIMEOUT_MS (1800000ms) and SweepStaleReviewsDeps interface
- 8 comprehensive unit tests covering all TASK-04 behaviors
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cherry-pick plan 01 task quality gate validation module and its
integration into IPC, API server, and automation service.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add SaveTaskResult type to contracts.ts (id + optional capabilityWarning)
- IPC handler: validate new tasks, reject with TASK_VALIDATION_FAILED, check capability match
- API server: validate POST /issues and meeting action items before db.saveTask
- Automation service: validate create_task action, skip with warning if invalid
- CapabilityMatchDb interface uses optional skill methods for graceful degradation
- TypeScript compiles cleanly across all process boundaries
- All 241 tests pass with zero regressions
- Add sessionHandoff field to PromptContext interface and Session Handoff section in buildSystemPrompt
- Add getSessionHandoff dependency to orchestration service, load handoff before prompt build
- Update orchestration to prefer task_sessions table over legacy tasks.session_json (SESS-01)
- Wire getSessionHandoff through ServiceGraph into createOrchestrationService
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add getActiveTaskSession, incrementSessionUsage, archiveSession, createNewSession,
getRecentRunsForTask, getRecentActivityForTask methods to AppDatabase
- Add nativeContextManagement optional boolean to ServerAdapterModule interface
- Set nativeContextManagement: true for claude_local and codex_local, false for gemini_local
- Add TaskSessionRecord interface to shared types
- Add unit tests for task_sessions CRUD and adapter exemption flags
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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)