Agents always replied with channel="direct" regardless of the original
message's channel, creating new DM conversations instead of replying
in the existing group chat (department/company/project/incident).
Root cause: three layers of the system guided agents toward direct-only
replies:
1. Prompt instructions (wake-reasons, IC/manager operating instructions)
hardcoded channel="direct" and toAgentId=fromAgentId for all replies
2. Standup API omitted toAgentId from unread messages, removing context
about who the original message was addressed to
3. Inbox projection contained no reply-routing metadata, so the agent's
LLM had no signal about which channel to respond in
Fixes:
- routes-agents-standup.ts: include toAgentId in unread message payload
- wake-reasons/en.ts: differentiate direct vs group channel reply format
- operating-en.ts: same channel-preserving guidance for IC and manager
- inbox-projection.ts: embed reply-to routing context in message header
so the LLM sees channel + target alongside the message body
The heartbeat path (canRunAgent → startAgentHeartbeatRun) only checked
isAgentPendingApproval (hire gate), completely bypassing plan_review,
draft_review, strategy_review and other blocking approval types. Agents
in Co-Founder mode continued running despite pending approvals.
Root cause: task-runner.prepareTaskRun had the full getBlockingApproval
check, but heartbeat runs never went through prepareTaskRun — they
created runs directly, skipping the approval gate entirely.
Three fixes:
- heartbeat.canRunAgent: add getBlockingApproval(companyId, agentId, null)
for agent-level blocking approvals
- heartbeat.startAgentHeartbeatRun: add getBlockingApproval with task id
after task resolution, before run creation
- task-runner.resumeQueuedRuns: add getBlockingApproval check so app
restart does not dispatch runs past pending approvals
Also replaced the hardcoded type list in hasPendingReviewApprovalForAgent
with a dynamic join on approval_policies.blocks_execution, eliminating
the sync risk when new blocking approval types are added.