From d75389c418b43e6930a0bae2b9356a2205cbaa7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Thu, 16 Apr 2026 23:37:42 -0400 Subject: [PATCH] refactor(prompt-builder): convert 11-key wake reason map to data table - New src/main/prompt-builder/wake-reasons/en.ts: WAKE_REASONS record of { title, steps(vars) } entries + renderWakeReason() formatter that numbers the steps and composes the final block - buildTaskPrompt in prompt-builder/index.ts collapses 100-line inline reasonMap into a single renderWakeReason call - index.ts shrinks from 161 to 77 lines; each wake reason's semantic content (title text + numbered action steps + embedded curl hints) preserved verbatim --- src/main/prompt-builder/index.ts | 94 +------------ src/main/prompt-builder/wake-reasons/en.ts | 148 +++++++++++++++++++++ 2 files changed, 153 insertions(+), 89 deletions(-) create mode 100644 src/main/prompt-builder/wake-reasons/en.ts diff --git a/src/main/prompt-builder/index.ts b/src/main/prompt-builder/index.ts index 87cdb3c5..f82286e3 100644 --- a/src/main/prompt-builder/index.ts +++ b/src/main/prompt-builder/index.ts @@ -1,6 +1,7 @@ import type { AgentRecord, AgentMessageRecord, ApprovalRecord, DocumentRecord, ExecutionMode, GoalRecord, KnowledgeEntryRecord, MeetingRecord, ProjectRecord, SprintRecord, TaskRecord } from "@shared/types"; import type { AppLocale } from "@shared/locale"; import { buildSystemPrompt } from "./system-prompt"; +import { renderWakeReason } from "./wake-reasons/en"; export interface PromptContext { agent: AgentRecord; @@ -50,95 +51,10 @@ export function buildTaskPrompt(ctx: PromptContext): string { const parts: string[] = [systemPrompt]; if (ctx.wakeReason) { - const reasonMap: Record = { - timer: `Regular heartbeat check-in. This is your periodic wake. -Action required: -1. Call GET /api/agents/me/standup to get your full briefing -2. Review all your assigned tasks and their statuses -3. Take action on the most important item: reviews > unblocking > new work -4. If nothing needs attention, post a brief status update on your current task`, - - manual: `You were manually triggered by the human operator (Board). -Action required: -1. Check your assigned tasks for any new assignments or updated instructions -2. Check your messages for any direct communications -3. Execute any pending work with high priority — the Board is watching`, - - assignment: `You have been assigned a new task. -Action required: -1. GET /api/companies/${ctx.agent.companyId}/issues?assigneeAgentId=${ctx.agent.id}&status=todo to find your new assignment -2. Review the task title, description, and any existing comments -3. POST /api/issues/{issueId}/checkout to claim the task -4. Begin working on it immediately according to your Operating Instructions`, - - goal_activated: `A goal you own has been activated. -Action required: -1. GET /api/companies/${ctx.agent.companyId}/goals to find the newly activated goal -2. Decompose the goal into projects (POST /api/companies/${ctx.agent.companyId}/projects) -3. Break projects into actionable tasks and assign to your team -4. Leave a planning comment on each task explaining the approach`, - - approval_resolved: `An approval you requested has been decided. -Action required: -1. GET /api/companies/${ctx.agent.companyId}/approvals?requestedByAgentId=${ctx.agent.id} to check the result -2. If APPROVED: proceed with the action you were waiting on (hiring, credential access, etc.) -3. If DENIED or REVISION_REQUESTED: read the feedback, adjust your approach, and resubmit if appropriate -4. Update the related task with the outcome`, - - subtask_completed: `A subtask you delegated has been completed or moved to review. -Action required: -1. Check tasks with status "in_review" from your reports -2. Read ALL comments on the completed subtask (GET /api/issues/{issueId}/comments) -3. Review the work quality against the acceptance criteria you set -4. If acceptable: PATCH /api/issues/{issueId} with status "done" and an approving comment -5. If changes needed: PATCH /api/issues/{issueId} with status "in_progress" and specific feedback -6. Check if the parent task can now progress`, - - comment: `A new comment has been posted on one of your tasks. -Action required: -1. Check your recent comments in the briefing above -2. If someone asked you a question: respond via POST /api/issues/{issueId}/comments -3. If your manager left feedback: address it and update the task -4. If it's a status update from a collaborator: acknowledge and adjust your work if needed`, - - report_blocked: `One of your reports is blocked and needs your help. -Action required: -1. GET /api/companies/${ctx.agent.companyId}/issues?status=blocked to find blocked tasks under your reports -2. Read the blocker comments to understand what they need -3. Take action to unblock them: - - If they need credentials: create a secret_access approval via POST /api/companies/${ctx.agent.companyId}/approvals - - If they need cross-department help: send a message to the relevant department lead - - If they need clarification: post a comment with clear direction -4. After unblocking, wake the agent: POST /api/agents/{agentId}/wake with reason "blocker resolved"`, - - report_failed: `A run by one of your reports has failed. -Action required: -1. Check your reports' task statuses for failed or stalled items -2. Investigate: read the task comments and any error information -3. Decide on next steps: - - If the task should be retried: set status back to "todo" with a comment explaining what to do differently - - If the task should be reassigned: change the assigneeAgentId to another capable report - - If the task is no longer needed: set status to "cancelled" with explanation -4. Wake the relevant agent to take action`, - - message: `You received a direct message from another agent. -Action required: -1. GET /api/companies/${ctx.agent.companyId}/messages?unreadOnly=true to read your new messages -2. Read each message and understand what is being asked -3. Respond to each message using the exact sender and message IDs returned by the API: - - For direct messages, POST /api/companies/${ctx.agent.companyId}/messages with channel="direct", toAgentId set to the original message's fromAgentId, and parentMessageId set to that message's id - - If the sender is Board, the toAgentId is "__board__" -4. Mark messages as read: POST /api/companies/${ctx.agent.companyId}/messages/{messageId}/read -5. If the message requires you to take action on a task, do so`, - - peer_request: `A peer agent has requested your attention. -Action required: -1. Check your messages: GET /api/companies/${ctx.agent.companyId}/messages?unreadOnly=true -2. Check for any new task assignments from outside your usual chain -3. Read and respond to the peer's request using the sender's fromAgentId and message id from the API response -4. If cross-department collaboration is needed, coordinate via messages and tasks`, - }; - const explanation = reasonMap[ctx.wakeReason] ?? `Trigger: ${ctx.wakeReason}\nAction required: Check your standup briefing and messages for context on why you were woken.`; + const explanation = renderWakeReason(ctx.wakeReason, { + companyId: ctx.agent.companyId, + agentId: ctx.agent.id, + }); parts.push(`\n# Wake Reason\n${explanation}\n`); } diff --git a/src/main/prompt-builder/wake-reasons/en.ts b/src/main/prompt-builder/wake-reasons/en.ts new file mode 100644 index 00000000..a6b9b1c4 --- /dev/null +++ b/src/main/prompt-builder/wake-reasons/en.ts @@ -0,0 +1,148 @@ +export type WakeReason = + | "timer" + | "manual" + | "assignment" + | "goal_activated" + | "approval_resolved" + | "subtask_completed" + | "comment" + | "report_blocked" + | "report_failed" + | "message" + | "peer_request"; + +export interface WakeReasonVars { + companyId: string; + agentId: string; +} + +export interface WakeReasonEntry { + title: string; + steps: (v: WakeReasonVars) => string[]; +} + +export const WAKE_REASONS: Record = { + timer: { + title: "Regular heartbeat check-in. This is your periodic wake.", + steps: () => [ + "Call GET /api/agents/me/standup to get your full briefing", + "Review all your assigned tasks and their statuses", + "Take action on the most important item: reviews > unblocking > new work", + "If nothing needs attention, post a brief status update on your current task", + ], + }, + manual: { + title: "You were manually triggered by the human operator (Board).", + steps: () => [ + "Check your assigned tasks for any new assignments or updated instructions", + "Check your messages for any direct communications", + "Execute any pending work with high priority — the Board is watching", + ], + }, + assignment: { + title: "You have been assigned a new task.", + steps: (v) => [ + `GET /api/companies/${v.companyId}/issues?assigneeAgentId=${v.agentId}&status=todo to find your new assignment`, + "Review the task title, description, and any existing comments", + "POST /api/issues/{issueId}/checkout to claim the task", + "Begin working on it immediately according to your Operating Instructions", + ], + }, + goal_activated: { + title: "A goal you own has been activated.", + steps: (v) => [ + `GET /api/companies/${v.companyId}/goals to find the newly activated goal`, + `Decompose the goal into projects (POST /api/companies/${v.companyId}/projects)`, + "Break projects into actionable tasks and assign to your team", + "Leave a planning comment on each task explaining the approach", + ], + }, + approval_resolved: { + title: "An approval you requested has been decided.", + steps: (v) => [ + `GET /api/companies/${v.companyId}/approvals?requestedByAgentId=${v.agentId} to check the result`, + "If APPROVED: proceed with the action you were waiting on (hiring, credential access, etc.)", + "If DENIED or REVISION_REQUESTED: read the feedback, adjust your approach, and resubmit if appropriate", + "Update the related task with the outcome", + ], + }, + subtask_completed: { + title: "A subtask you delegated has been completed or moved to review.", + steps: () => [ + 'Check tasks with status "in_review" from your reports', + "Read ALL comments on the completed subtask (GET /api/issues/{issueId}/comments)", + "Review the work quality against the acceptance criteria you set", + 'If acceptable: PATCH /api/issues/{issueId} with status "done" and an approving comment', + 'If changes needed: PATCH /api/issues/{issueId} with status "in_progress" and specific feedback', + "Check if the parent task can now progress", + ], + }, + comment: { + title: "A new comment has been posted on one of your tasks.", + steps: () => [ + "Check your recent comments in the briefing above", + "If someone asked you a question: respond via POST /api/issues/{issueId}/comments", + "If your manager left feedback: address it and update the task", + "If it's a status update from a collaborator: acknowledge and adjust your work if needed", + ], + }, + report_blocked: { + title: "One of your reports is blocked and needs your help.", + steps: (v) => [ + `GET /api/companies/${v.companyId}/issues?status=blocked to find blocked tasks under your reports`, + "Read the blocker comments to understand what they need", + `Take action to unblock them: + - If they need credentials: create a secret_access approval via POST /api/companies/${v.companyId}/approvals + - If they need cross-department help: send a message to the relevant department lead + - If they need clarification: post a comment with clear direction`, + 'After unblocking, wake the agent: POST /api/agents/{agentId}/wake with reason "blocker resolved"', + ], + }, + report_failed: { + title: "A run by one of your reports has failed.", + steps: () => [ + "Check your reports' task statuses for failed or stalled items", + "Investigate: read the task comments and any error information", + `Decide on next steps: + - If the task should be retried: set status back to "todo" with a comment explaining what to do differently + - If the task should be reassigned: change the assigneeAgentId to another capable report + - If the task is no longer needed: set status to "cancelled" with explanation`, + "Wake the relevant agent to take action", + ], + }, + message: { + title: "You received a direct message from another agent.", + steps: (v) => [ + `GET /api/companies/${v.companyId}/messages?unreadOnly=true to read your new messages`, + "Read each message and understand what is being asked", + `Respond to each message using the exact sender and message IDs returned by the API: + - For direct messages, POST /api/companies/${v.companyId}/messages with channel="direct", toAgentId set to the original message's fromAgentId, and parentMessageId set to that message's id + - If the sender is Board, the toAgentId is "__board__"`, + `Mark messages as read: POST /api/companies/${v.companyId}/messages/{messageId}/read`, + "If the message requires you to take action on a task, do so", + ], + }, + peer_request: { + title: "A peer agent has requested your attention.", + steps: (v) => [ + `Check your messages: GET /api/companies/${v.companyId}/messages?unreadOnly=true`, + "Check for any new task assignments from outside your usual chain", + "Read and respond to the peer's request using the sender's fromAgentId and message id from the API response", + "If cross-department collaboration is needed, coordinate via messages and tasks", + ], + }, +}; + +export function renderWakeReason( + reason: string, + vars: WakeReasonVars, +): string { + const entry = WAKE_REASONS[reason as WakeReason]; + if (!entry) { + return `Trigger: ${reason}\nAction required: Check your standup briefing and messages for context on why you were woken.`; + } + const steps = entry.steps(vars) + .map((step, i) => `${i + 1}. ${step}`) + .join("\n"); + return `${entry.title}\nAction required:\n${steps}`; +}