diff --git a/src/router/ackMessageGenerator.ts b/src/router/ackMessageGenerator.ts index 7e400d1a..0db5a39d 100644 --- a/src/router/ackMessageGenerator.ts +++ b/src/router/ackMessageGenerator.ts @@ -18,7 +18,15 @@ import { logger } from '../utils/logging.js'; // --------------------------------------------------------------------------- const ACK_SYSTEM_PROMPT = `You write brief, casual acknowledgment messages for an AI coding bot. The goal is to buy time — let the user know you've seen their request while work kicks off in the background. -Keep it under 20 words. Start with a single relevant emoji. Be conversational and natural — like a friendly coworker responding in chat. Reference the specific topic from the context (e.g. "the chart library question", "that auth bug", "the dark mode feature"). Never say "Understood", "I will", or "I'll be working on". Use casual buying-time phrasing like "Just a moment, let me look into...", "On it — checking the...", "Give me a sec, pulling up...", "Looking into the... now", "Let me dig into...". No markdown formatting. No period at the end.`; +Keep it under 20 words. Start with a single relevant emoji. Be conversational and natural — like a friendly coworker responding in chat. Reference the specific topic from the context (e.g. "the chart library question", "that auth bug", "the dark mode feature"). Never say "Understood", "I will", or "I'll be working on". No markdown formatting. No period at the end. + +CRITICAL: Match the action verb to the agent's role. Use role-appropriate phrasing: +- Implementation agent: "On it — starting work on...", "Getting to work on...", "Building the...", "Coding up the..." +- Planning agent: "Mapping out...", "Designing the plan for...", "Sketching out the approach for...", "Planning the..." +- Review agent: "On it — checking the...", "Examining the...", "Looking over the...", "Reviewing the..." +- Splitting agent: "Breaking down...", "Splitting up...", "Carving out the tasks for..." +- Debug agent: "Digging into the logs for...", "Tracing the issue in...", "Investigating the..." +- Feedback/respond agent: "Reading through the feedback on...", "Going through the comments on..."`; // --------------------------------------------------------------------------- // Context extractors — pull relevant snippets from webhook payloads @@ -241,7 +249,7 @@ async function callAckModel( ): Promise { const client = new LLMist({ customModels: CUSTOM_MODELS as ModelSpec[] }); const roleHint = AGENT_ROLE_HINTS[agentType] ?? 'Processes the request'; - const userPrompt = `Agent type: ${agentType}\nAgent role: ${roleHint}\n\nRequest context:\n${contextSnippet}`; + const userPrompt = `Agent type: ${agentType}\nAgent role: ${roleHint}\n\nYour message MUST reflect the "${agentType}" agent's role: "${roleHint}". Use action language appropriate for this specific role.\n\nRequest context:\n${contextSnippet}`; const result = await client.text.complete(userPrompt, { model, diff --git a/tests/unit/router/ackMessageGenerator.test.ts b/tests/unit/router/ackMessageGenerator.test.ts index d9d8613a..09aa6972 100644 --- a/tests/unit/router/ackMessageGenerator.test.ts +++ b/tests/unit/router/ackMessageGenerator.test.ts @@ -384,6 +384,69 @@ describe('generateAckMessage', () => { expect(process.env.OPENROUTER_API_KEY).toBe(originalKey); }); + it('passes role hint in user prompt for implementation agent', async () => { + setupHappyPath('🚀 Starting work on the dark mode feature'); + + await generateAckMessage('implementation', 'Card: Add dark mode support', 'p1'); + + const callArgs = mockTextComplete.mock.calls[0]; + const userPrompt: string = callArgs[0]; + expect(userPrompt).toContain('Agent type: implementation'); + expect(userPrompt).toContain( + 'Agent role: Writes code, runs tests, and prepares a pull request', + ); + expect(userPrompt).toContain( + 'Your message MUST reflect the "implementation" agent\'s role: "Writes code, runs tests, and prepares a pull request"', + ); + }); + + it('passes role hint in user prompt for review agent', async () => { + setupHappyPath('🔍 Reviewing the PR changes'); + + await generateAckMessage('review', 'PR: feat: add dark mode', 'p1'); + + const callArgs = mockTextComplete.mock.calls[0]; + const userPrompt: string = callArgs[0]; + expect(userPrompt).toContain('Agent type: review'); + expect(userPrompt).toContain( + 'Agent role: Reviews pull request changes for quality and correctness', + ); + expect(userPrompt).toContain( + 'Your message MUST reflect the "review" agent\'s role: "Reviews pull request changes for quality and correctness"', + ); + }); + + it('passes role hint in user prompt for splitting agent', async () => { + setupHappyPath('📋 Breaking down the tasks'); + + await generateAckMessage('splitting', 'Card: Split auth feature', 'p1'); + + const callArgs = mockTextComplete.mock.calls[0]; + const userPrompt: string = callArgs[0]; + expect(userPrompt).toContain('Agent type: splitting'); + expect(userPrompt).toContain( + 'Agent role: Breaks down a feature plan into smaller, ordered work items (subtasks)', + ); + expect(userPrompt).toContain( + 'Your message MUST reflect the "splitting" agent\'s role: "Breaks down a feature plan into smaller, ordered work items (subtasks)"', + ); + }); + + it('system prompt includes role-categorized example phrases', async () => { + setupHappyPath('🚀 Getting to work on this'); + + await generateAckMessage('implementation', 'Card: Add feature', 'p1'); + + const callArgs = mockTextComplete.mock.calls[0]; + const options = callArgs[1]; + const systemPrompt: string = options.systemPrompt; + // Verify system prompt has role-category examples + expect(systemPrompt).toContain('Implementation agent'); + expect(systemPrompt).toContain('Planning agent'); + expect(systemPrompt).toContain('Review agent'); + expect(systemPrompt).toContain("CRITICAL: Match the action verb to the agent's role"); + }); + it('falls back to static message on timeout', async () => { vi.useFakeTimers(); diff --git a/web/src/lib/org-context.tsx b/web/src/lib/org-context.tsx index 146fd979..54cde194 100644 --- a/web/src/lib/org-context.tsx +++ b/web/src/lib/org-context.tsx @@ -30,7 +30,7 @@ export function OrgProvider({ me, }: { children: React.ReactNode; me: MeData | undefined }) { const [effectiveOrgId, setEffectiveOrgId] = useState(null); - const isAdmin = me?.role === 'admin'; + const isAdmin = me?.role === 'admin' || me?.role === 'superadmin'; const initialized = useRef(false); // Initialize from me data + localStorage