fix(team): recognize native bootstrap control envelopes
This commit is contained in:
parent
2080e86f44
commit
ac2b6c9352
6 changed files with 113 additions and 6 deletions
|
|
@ -135,7 +135,7 @@ export interface InternalControlMessageDisplay {
|
|||
}
|
||||
|
||||
export function getInternalControlMessageDisplay(
|
||||
message: Pick<InboxMessage, 'text'> & Partial<Pick<InboxMessage, 'source'>>
|
||||
message: Pick<InboxMessage, 'text'> & Partial<Pick<InboxMessage, 'from' | 'source'>>
|
||||
): InternalControlMessageDisplay | null {
|
||||
if (!isTeamInternalControlMessageEnvelope(message)) {
|
||||
return null;
|
||||
|
|
@ -237,7 +237,7 @@ export function getBootstrapAcknowledgementDisplay(
|
|||
}
|
||||
|
||||
export function getSanitizedInboxMessageText(
|
||||
message: Pick<InboxMessage, 'text' | 'to'> & Partial<Pick<InboxMessage, 'source'>>
|
||||
message: Pick<InboxMessage, 'text' | 'to'> & Partial<Pick<InboxMessage, 'from' | 'source'>>
|
||||
): string {
|
||||
return (
|
||||
getInternalControlMessageDisplay(message)?.body ??
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ const INTERNAL_CONTROL_MESSAGE_SOURCES = new Set([
|
|||
'runtime_delivery',
|
||||
'system_notification',
|
||||
]);
|
||||
const INTERNAL_BOOTSTRAP_AUTHORS = new Set(['team-lead', 'lead', 'orchestrator']);
|
||||
|
||||
export function stripTranscriptSpeakerPrefix(value: string): string {
|
||||
let normalized = value.trim();
|
||||
|
|
@ -22,7 +23,7 @@ export function stripTranscriptSpeakerPrefix(value: string): string {
|
|||
export function isNativeAppManagedBootstrapCheckText(value: unknown): boolean {
|
||||
return (
|
||||
typeof value === 'string' &&
|
||||
stripTranscriptSpeakerPrefix(value).includes(NATIVE_APP_MANAGED_BOOTSTRAP_CHECK_OPEN)
|
||||
stripTranscriptSpeakerPrefix(value).startsWith(NATIVE_APP_MANAGED_BOOTSTRAP_CHECK_OPEN)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +57,17 @@ export function isTeamInternalControlMessageText(value: unknown): boolean {
|
|||
export function isTeamInternalControlMessageEnvelope(message: {
|
||||
text?: unknown;
|
||||
source?: unknown;
|
||||
from?: unknown;
|
||||
}): boolean {
|
||||
if (isNativeAppManagedBootstrapCheckText(message.text)) {
|
||||
if (typeof message.source === 'string') {
|
||||
return INTERNAL_CONTROL_MESSAGE_SOURCES.has(message.source);
|
||||
}
|
||||
return (
|
||||
typeof message.from === 'string' &&
|
||||
INTERNAL_BOOTSTRAP_AUTHORS.has(message.from.trim().toLowerCase())
|
||||
);
|
||||
}
|
||||
if (!isTeamInternalControlMessageText(message.text)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,8 +79,10 @@ describe('TeamMessageFeedService', () => {
|
|||
getConfig: vi.fn(async () => config),
|
||||
getInboxMessages: vi.fn(async () => [
|
||||
makeMessage({
|
||||
from: 'team-lead',
|
||||
to: undefined,
|
||||
messageId: 'native-bootstrap-private-check',
|
||||
source: 'system_notification',
|
||||
source: undefined,
|
||||
text: '<agent_teams_native_app_managed_bootstrap_check>\nprivate\n</agent_teams_native_app_managed_bootstrap_check>',
|
||||
}),
|
||||
makeMessage({
|
||||
|
|
@ -123,6 +125,27 @@ Messages:
|
|||
expect(feed.messages.map((message) => message.messageId)).toEqual(['quoted-control-prompt']);
|
||||
});
|
||||
|
||||
it('does not hide user-authored native bootstrap marker quotes from the feed', async () => {
|
||||
const service = new TeamMessageFeedService({
|
||||
getConfig: vi.fn(async () => config),
|
||||
getInboxMessages: vi.fn(async () => [
|
||||
makeMessage({
|
||||
messageId: 'quoted-native-bootstrap-control',
|
||||
source: 'user_sent',
|
||||
text: '<agent_teams_native_app_managed_bootstrap_check>\nquoted\n</agent_teams_native_app_managed_bootstrap_check>',
|
||||
}),
|
||||
]),
|
||||
getLeadSessionMessages: vi.fn(async () => []),
|
||||
getSentMessages: vi.fn(async () => []),
|
||||
});
|
||||
|
||||
const feed = await service.getFeed('signal-ops-4');
|
||||
|
||||
expect(feed.messages.map((message) => message.messageId)).toEqual([
|
||||
'quoted-native-bootstrap-control',
|
||||
]);
|
||||
});
|
||||
|
||||
it('refreshes the durable feed after cache expiry even when the dirty signal was missed', async () => {
|
||||
let inboxMessages: InboxMessage[] = [makeMessage()];
|
||||
const getInboxMessages = vi.fn(async () => inboxMessages);
|
||||
|
|
|
|||
|
|
@ -71,13 +71,32 @@ Do NOT send acknowledgement-only messages such as "ready" or "online".`);
|
|||
`<agent_teams_native_app_managed_bootstrap_check>
|
||||
Your Agent Teams startup context was already loaded by the app.
|
||||
</agent_teams_native_app_managed_bootstrap_check>`,
|
||||
{ source: 'system_notification' }
|
||||
{ source: undefined }
|
||||
);
|
||||
|
||||
expect(getInternalControlMessageDisplay(message)?.summary).toBe('Internal bootstrap check');
|
||||
expect(getSanitizedInboxMessageText(message)).toBe('Internal bootstrap check hidden in the UI.');
|
||||
});
|
||||
|
||||
it('does not sanitize user-authored native bootstrap marker quotes', () => {
|
||||
const text = `<agent_teams_native_app_managed_bootstrap_check>
|
||||
Your Agent Teams startup context was already loaded by the app.
|
||||
</agent_teams_native_app_managed_bootstrap_check>`;
|
||||
const message = makeMessage(text, { from: 'user', source: 'user_sent' });
|
||||
|
||||
expect(getInternalControlMessageDisplay(message)).toBeNull();
|
||||
expect(getSanitizedInboxMessageText(message)).toBe(text);
|
||||
});
|
||||
|
||||
it('does not sanitize visible lead text that only mentions the native bootstrap marker', () => {
|
||||
const text =
|
||||
'Visible note quoting <agent_teams_native_app_managed_bootstrap_check> for diagnostics.';
|
||||
const message = makeMessage(text, { from: 'team-lead', source: 'lead_process' });
|
||||
|
||||
expect(getInternalControlMessageDisplay(message)).toBeNull();
|
||||
expect(getSanitizedInboxMessageText(message)).toBe(text);
|
||||
});
|
||||
|
||||
it('sanitizes leaked lead inbox relay prompts defensively', () => {
|
||||
const message = makeMessage(
|
||||
`Human: You have new inbox messages addressed to you (team lead "team-lead").
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ describe('filterTeamMessages', () => {
|
|||
const messages = [
|
||||
makeMessage({
|
||||
messageId: 'native-bootstrap-private-check',
|
||||
source: 'system_notification',
|
||||
source: undefined,
|
||||
text: '<agent_teams_native_app_managed_bootstrap_check>\nprivate\n</agent_teams_native_app_managed_bootstrap_check>',
|
||||
}),
|
||||
makeMessage({
|
||||
|
|
@ -59,6 +59,25 @@ describe('filterTeamMessages', () => {
|
|||
expect(result.map((message) => message.messageId)).toEqual(['visible-message']);
|
||||
});
|
||||
|
||||
it('keeps user-authored native bootstrap marker quotes visible', () => {
|
||||
const messages = [
|
||||
makeMessage({
|
||||
from: 'user',
|
||||
messageId: 'user-native-bootstrap-quote',
|
||||
source: 'user_sent',
|
||||
text: '<agent_teams_native_app_managed_bootstrap_check>\nquoted\n</agent_teams_native_app_managed_bootstrap_check>',
|
||||
}),
|
||||
];
|
||||
|
||||
const result = filterTeamMessages(messages, {
|
||||
timeWindow: null,
|
||||
filter: { from: new Set(), to: new Set(), showNoise: true },
|
||||
searchQuery: '',
|
||||
});
|
||||
|
||||
expect(result.map((message) => message.messageId)).toEqual(['user-native-bootstrap-quote']);
|
||||
});
|
||||
|
||||
it('hides leaked lead inbox relay prompt echoes', () => {
|
||||
const messages = [
|
||||
makeMessage({
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ Messages:
|
|||
Timestamp: 2026-05-06T15:02:54.853Z
|
||||
Text:
|
||||
#f8d7235a done.`;
|
||||
const nativeBootstrapPrompt = `<agent_teams_native_app_managed_bootstrap_check>
|
||||
Your Agent Teams startup context was already loaded by the app.
|
||||
</agent_teams_native_app_managed_bootstrap_check>`;
|
||||
|
||||
describe('teamInternalControlMessages', () => {
|
||||
it('detects lead inbox relay prompts and Human-prefixed echoes', () => {
|
||||
|
|
@ -60,6 +63,38 @@ describe('teamInternalControlMessages', () => {
|
|||
text: `Human: ${leadRelayPrompt}`,
|
||||
})
|
||||
).toBe(false);
|
||||
expect(
|
||||
isTeamInternalControlMessageEnvelope({
|
||||
text: nativeBootstrapPrompt,
|
||||
from: 'team-lead',
|
||||
})
|
||||
).toBe(true);
|
||||
expect(
|
||||
isTeamInternalControlMessageEnvelope({
|
||||
text: nativeBootstrapPrompt,
|
||||
from: 'orchestrator',
|
||||
})
|
||||
).toBe(true);
|
||||
expect(isTeamInternalControlMessageText(`Human: ${nativeBootstrapPrompt}`)).toBe(true);
|
||||
expect(
|
||||
isTeamInternalControlMessageEnvelope({
|
||||
source: 'lead_process',
|
||||
text: `Visible note quoting ${nativeBootstrapPrompt}`,
|
||||
})
|
||||
).toBe(false);
|
||||
expect(
|
||||
isTeamInternalControlMessageEnvelope({
|
||||
source: 'user_sent',
|
||||
text: nativeBootstrapPrompt,
|
||||
from: 'user',
|
||||
})
|
||||
).toBe(false);
|
||||
expect(
|
||||
isTeamInternalControlMessageEnvelope({
|
||||
text: nativeBootstrapPrompt,
|
||||
from: 'user',
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('strips an exact echoed control prefix while preserving visible trailing text', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue