fix(team): support runtime bootstrap prompt sanitizing

This commit is contained in:
iliya 2026-04-07 01:33:04 +03:00
parent 0064380160
commit ac3475d3be
2 changed files with 74 additions and 7 deletions

View file

@ -2,15 +2,28 @@ import { displayMemberName } from '@renderer/utils/memberHelpers';
import type { InboxMessage } from '@shared/types';
const BOOTSTRAP_REQUIRED_MARKERS = [
'Your FIRST action: call MCP tool member_briefing',
'Do NOT start work, claim tasks, or improvise workflow/task/process rules before member_briefing succeeds.',
const BOOTSTRAP_REQUIRED_MARKER_SETS = [
[
'Your FIRST action: call MCP tool member_briefing',
'Do NOT start work, claim tasks, or improvise workflow/task/process rules before member_briefing succeeds.',
],
[
'Your FIRST action: call MCP tool member_briefing',
'The team has already been created and you are being attached as a persistent teammate.',
],
[
'Your FIRST action: call MCP tool member_briefing',
'The team has already been reconnected and you are being re-attached as a persistent teammate.',
],
] as const;
const BOOTSTRAP_SUPPORTING_MARKERS = [
'If member_briefing fails, send',
'member_briefing is expected to be available in your initial MCP tool list.',
'IMPORTANT: When sending messages to the team lead',
'Call member_briefing directly yourself. Do NOT use Agent',
'wait for instructions from the lead and use team mailbox/task tools normally',
'resume your queue normally and prioritize already-assigned board work',
] as const;
type TeamProviderId = 'anthropic' | 'codex' | 'gemini';
@ -136,7 +149,9 @@ export function getBootstrapPromptDisplay(
message: Pick<InboxMessage, 'text' | 'to'>
): BootstrapPromptDisplay | null {
const text = typeof message.text === 'string' ? message.text.trim() : '';
const hasRequiredMarkers = BOOTSTRAP_REQUIRED_MARKERS.every((marker) => text.includes(marker));
const hasRequiredMarkers = BOOTSTRAP_REQUIRED_MARKER_SETS.some((markerSet) =>
markerSet.every((marker) => text.includes(marker))
);
const hasSupportingMarker = BOOTSTRAP_SUPPORTING_MARKERS.some((marker) => text.includes(marker));
if (!text.startsWith('You are ') || !hasRequiredMarkers || !hasSupportingMarker) {
return null;
@ -147,10 +162,10 @@ export function getBootstrapPromptDisplay(
(typeof message.to === 'string' ? message.to.trim() : undefined);
const teamName = matchField(text, /on team "([^"]+)"/);
const providerId = parseProviderId(
matchField(text, /Provider override for this teammate:\s*([^\.\n]+)/i)
matchField(text, /Provider override(?: for this teammate)?:\s*([^\.\n]+)/i)
);
const model = matchField(text, /Model override for this teammate:\s*([^\.\n]+)/i);
const effort = matchField(text, /Effort override for this teammate:\s*([^\.\n]+)/i);
const model = matchField(text, /Model override(?: for this teammate)?:\s*([^\.\n]+)/i);
const effort = matchField(text, /Effort override(?: for this teammate)?:\s*([^\.\n]+)/i);
const runtime = buildRuntimeSummary(providerId, model, effort);
const displayName = teammateName ? displayMemberName(teammateName) : 'teammate';
const summary = `Starting ${displayName}`;

View file

@ -0,0 +1,52 @@
import { describe, expect, it } from 'vitest';
import {
getBootstrapPromptDisplay,
getSanitizedInboxMessageText,
} from '@renderer/utils/bootstrapPromptSanitizer';
import type { InboxMessage } from '@shared/types';
function makeMessage(text: string, overrides: Partial<InboxMessage> = {}): InboxMessage {
return {
from: 'team-lead',
to: 'alice',
text,
timestamp: '2026-04-07T10:00:00.000Z',
read: false,
messageId: 'msg-1',
...overrides,
};
}
describe('bootstrapPromptSanitizer', () => {
it('sanitizes legacy verbose bootstrap prompts', () => {
const message = makeMessage(`You are alice, a reviewer on team "forge-labs" (forge-labs).
Your FIRST action: call MCP tool member_briefing with:
{ teamName: "forge-labs", memberName: "alice" }
member_briefing is expected to be available in your initial MCP tool list.
Do NOT start work, claim tasks, or improvise workflow/task/process rules before member_briefing succeeds.
If member_briefing fails, send one short natural-language message to your team lead "team-lead".
IMPORTANT: When sending messages to the team lead, always use the exact name "team-lead".`);
const display = getBootstrapPromptDisplay(message);
expect(display?.summary).toBe('Starting alice');
expect(getSanitizedInboxMessageText(message)).toContain('Lead is starting `alice` as a teammate.');
});
it('sanitizes new runtime-generated bootstrap prompts', () => {
const message = makeMessage(`You are alice, a reviewer on team "forge-labs" (forge-labs).
IMPORTANT: Communicate in English. All messages, summaries, and task descriptions MUST be in English.
The team has already been created and you are being attached as a persistent teammate.
Your FIRST action: call MCP tool member_briefing with:
{ teamName: "forge-labs", memberName: "alice" }
Call member_briefing directly yourself. Do NOT use Agent, any subagent, or a delegated helper for this bootstrap step.
If member_briefing fails, send one short natural-language message to "team-lead" with the exact error text.
After member_briefing succeeds, wait for instructions from the lead and use team mailbox/task tools normally.
Do NOT send acknowledgement-only messages such as "ready" or "online".`);
const display = getBootstrapPromptDisplay(message);
expect(display?.summary).toBe('Starting alice');
expect(getSanitizedInboxMessageText(message)).toContain('Startup instructions are hidden in the UI.');
});
});