diff --git a/src/main/api-server/routes-agents-standup.ts b/src/main/api-server/routes-agents-standup.ts index 2544a7df..35731ac3 100644 --- a/src/main/api-server/routes-agents-standup.ts +++ b/src/main/api-server/routes-agents-standup.ts @@ -38,6 +38,17 @@ export function registerAgentStandupRoute(server: AgentApiServer): void { const myPendingApprovals = db.listAgentPendingApprovals(token.companyId, token.agentId); const myResolvedApprovals = db.listAgentResolvedApprovals(token.companyId, token.agentId, 5); + // Unread inbox lives in the standup briefing as the agent's + // canonical view of pending communication. Pre-fix the agent had + // to make a second API call to /messages?unreadOnly=true that the + // model frequently skipped, so messages from peers and managers + // sat dark while the agent posted heartbeat status comments. + const unreadInbox = db.listAgentInboxUnread(token.companyId, token.agentId); + const senderIds = new Set(unreadInbox.map((m) => m.fromAgentId)); + const senderNames = senderIds.size > 0 + ? new Map(db.listAgentNamesByIds(token.companyId, Array.from(senderIds)).map((row) => [row.id, row.name])) + : new Map(); + const reportNameMap = new Map(directReports.map((a) => [a.id, a.name])); const taskTitleMap = new Map([ ...myTasks.map<[string, string]>((t) => [t.id, t.title]), @@ -48,6 +59,7 @@ export function registerAgentStandupRoute(server: AgentApiServer): void { server.json(res, 200, { agent: { id: agent.id, name: agent.name, role: agent.role, status: agent.status }, summary: { + unreadMessages: unreadInbox.length, tasksAwaitingReview: tasksAwaitingMyReview.length, blockedReportTasks: blockedReportTasks.length, myBlockedTasks: myBlockedTasks.length, @@ -56,6 +68,18 @@ export function registerAgentStandupRoute(server: AgentApiServer): void { pendingApprovals: myPendingApprovals.length, directReports: directReports.length, }, + unreadMessages: unreadInbox.map((m) => ({ + id: m.id, + from: senderNames.get(m.fromAgentId) ?? m.fromAgentId, + fromAgentId: m.fromAgentId, + channel: m.channel, + channelTargetId: m.channelTargetId, + subject: m.subject, + bodyPreview: m.body.slice(0, 240), + priority: m.priority, + parentMessageId: m.parentMessageId, + createdAt: m.createdAt, + })), tasksAwaitingReview: tasksAwaitingMyReview.map((t) => ({ id: t.id, title: t.title, diff --git a/src/main/prompt-builder/ceo-modes/operating.en.md b/src/main/prompt-builder/ceo-modes/operating.en.md index 3fec46ed..ef331f83 100644 --- a/src/main/prompt-builder/ceo-modes/operating.en.md +++ b/src/main/prompt-builder/ceo-modes/operating.en.md @@ -4,13 +4,14 @@ You are the top executive running a fully automated company. You make real decis ## Priority 1: Standup & Situational Assessment (EVERY wake) Before doing anything else, assess the state of the company: -1. GET /api/agents/me/standup — get your full briefing in one call -2. **CHECK MESSAGES FIRST**: GET /api/companies/{{companyId}}/messages?unreadOnly=true&limit=20 — read and respond to ALL unread messages from your team. Messages from department heads are high priority. +1. GET /api/agents/me/standup — your full briefing in ONE call. The response carries `unreadMessages`, `recentDocuments`, and `recentWorkProducts` alongside the existing task / approval / report sections, so you do not need a separate `?unreadOnly=true` request. +2. **MESSAGES FIRST**: walk `unreadMessages` from the standup. For each row, send a real reply (POST /api/companies/{{companyId}}/messages with channel="direct", toAgentId = `fromAgentId`, parentMessageId = `id`) AND mark it read (POST /api/companies/{{companyId}}/messages/{messageId}/read). Status comments do not count as replies. Department-head messages are top priority. 3. GET /api/companies/{{companyId}}/issues?status=in_review — check for work awaiting YOUR review 4. GET /api/companies/{{companyId}}/issues?status=blocked — check for blocked tasks needing escalation 5. Check your pending approvals for any resolved items 6. Read recent comments on your owned tasks -7. Assess company health: Are goals progressing? Are teams blocked? Is anyone idle? +7. Cross-check `recentDocuments` and `recentWorkProducts`: if a task says "I wrote X" but X is not in either list, the persistence call was missed — re-do the POST before reporting completion. +8. Assess company health: Are goals progressing? Are teams blocked? Is anyone idle? If there are NO active goals, this is your top priority — create strategic goals: curl -s -X POST "$AGENT_COMPANY_API_URL/api/companies/{{companyId}}/goals" \ diff --git a/src/main/prompt-builder/messages/operating-en.ts b/src/main/prompt-builder/messages/operating-en.ts index f5080820..bcefd237 100644 --- a/src/main/prompt-builder/messages/operating-en.ts +++ b/src/main/prompt-builder/messages/operating-en.ts @@ -118,8 +118,8 @@ export function icInstructions(companyId: string, agentId: string): string[] { You are an individual contributor — a skilled specialist in an automated company. You do real work and take real actions via the API. Follow these SOPs: ## Priority 1: Check for Feedback & New Assignments (EVERY wake — SOP-001) -1. GET /api/agents/me/standup — get your full briefing -2. **CHECK MESSAGES FIRST**: GET /api/companies/${companyId}/messages?unreadOnly=true&limit=20 — read and respond to ALL unread messages. If a colleague or manager needs something from you, prioritize it. +1. GET /api/agents/me/standup — get your full briefing in ONE call. The response includes \`unreadMessages\`, \`recentDocuments\`, \`recentWorkProducts\`, and every other state you need to plan this turn. +2. **MESSAGES FIRST**: walk \`unreadMessages\` from the standup. For each row, post a real reply (POST /api/companies/${companyId}/messages with channel="direct", toAgentId set to that row's \`fromAgentId\`, parentMessageId set to that row's \`id\`) AND mark it read (POST /api/companies/${companyId}/messages/{messageId}/read). Status comments do not count as replies. 3. Read ALL new comments on your assigned tasks (GET /api/issues/{issueId}/comments) 4. If your manager has set a task back to "in_progress": read their feedback and address it FIRST 5. If there are new comments with questions: respond promptly via POST /api/issues/{issueId}/comments