Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/router/ackMessageGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -241,7 +249,7 @@ async function callAckModel(
): Promise<string> {
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,
Expand Down
63 changes: 63 additions & 0 deletions tests/unit/router/ackMessageGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
2 changes: 1 addition & 1 deletion web/src/lib/org-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function OrgProvider({
me,
}: { children: React.ReactNode; me: MeData | undefined }) {
const [effectiveOrgId, setEffectiveOrgId] = useState<string | null>(null);
const isAdmin = me?.role === 'admin';
const isAdmin = me?.role === 'admin' || me?.role === 'superadmin';
const initialized = useRef(false);

// Initialize from me data + localStorage
Expand Down