Surface unread inbox in the standup briefing

Pre-fix the agent had to make a second `?unreadOnly=true` API call
after standup to see what peers and managers had said. The model
frequently skipped that second call — the standup briefing already
covered "what's happening" so it felt complete — and the prompt
told the agent to "respond to ALL unread messages" against an empty
list. Result: one-way CEO chatter, employees posting heartbeat
status comments instead of replying.

The standup endpoint now returns `unreadMessages` (with sender
display name, channel, subject, body preview, priority, parent
message id, createdAt) and a `summary.unreadMessages` count. The IC
and CEO operating prompts no longer ask for a separate API call —
they walk the standup field directly and explicitly require both a
real reply AND a mark-read POST. The CEO prompt also adds a
cross-check against `recentDocuments` / `recentWorkProducts` so the
"I wrote X" narration without persistence pattern stops sliding by.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-19 18:36:50 -04:00
1 parent f30ec740d8
commit 4eaad1f91a
3 files changed
+30 -5

No files matched your search

@@ -38,6 +38,17 @@ export function registerAgentStandupRoute(server: AgentApiServer): void {
const myPendingApprovals = db.listAgentPendingApprovals(token.companyId, token.agentId); const myPendingApprovals = db.listAgentPendingApprovals(token.companyId, token.agentId);
const myResolvedApprovals = db.listAgentResolvedApprovals(token.companyId, token.agentId, 5); 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<string, string>();
const reportNameMap = new Map(directReports.map((a) => [a.id, a.name])); const reportNameMap = new Map(directReports.map((a) => [a.id, a.name]));
const taskTitleMap = new Map<string, string>([ const taskTitleMap = new Map<string, string>([
...myTasks.map<[string, string]>((t) => [t.id, t.title]), ...myTasks.map<[string, string]>((t) => [t.id, t.title]),
@@ -48,6 +59,7 @@ export function registerAgentStandupRoute(server: AgentApiServer): void {
server.json(res, 200, { server.json(res, 200, {
agent: { id: agent.id, name: agent.name, role: agent.role, status: agent.status }, agent: { id: agent.id, name: agent.name, role: agent.role, status: agent.status },
summary: { summary: {
unreadMessages: unreadInbox.length,
tasksAwaitingReview: tasksAwaitingMyReview.length, tasksAwaitingReview: tasksAwaitingMyReview.length,
blockedReportTasks: blockedReportTasks.length, blockedReportTasks: blockedReportTasks.length,
myBlockedTasks: myBlockedTasks.length, myBlockedTasks: myBlockedTasks.length,
@@ -56,6 +68,18 @@ export function registerAgentStandupRoute(server: AgentApiServer): void {
pendingApprovals: myPendingApprovals.length, pendingApprovals: myPendingApprovals.length,
directReports: directReports.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) => ({ tasksAwaitingReview: tasksAwaitingMyReview.map((t) => ({
id: t.id, id: t.id,
title: t.title, title: t.title,
@@ -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) ## Priority 1: Standup & Situational Assessment (EVERY wake)
Before doing anything else, assess the state of the company: Before doing anything else, assess the state of the company:
1. GET /api/agents/me/standup — get your full briefing in one call 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. **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. 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 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 4. GET /api/companies/{{companyId}}/issues?status=blocked — check for blocked tasks needing escalation
5. Check your pending approvals for any resolved items 5. Check your pending approvals for any resolved items
6. Read recent comments on your owned tasks 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: 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" \ curl -s -X POST "$AGENT_COMPANY_API_URL/api/companies/{{companyId}}/goals" \
@@ -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: 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) ## Priority 1: Check for Feedback & New Assignments (EVERY wake — SOP-001)
1. GET /api/agents/me/standup — get your full briefing 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. **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. 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) 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 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 5. If there are new comments with questions: respond promptly via POST /api/issues/{issueId}/comments