fix(security): relay security_event through processAgentEvent

When the sidebar-agent fires security_event (canary leak, pre-spawn ML
block, tool-result ML block), it POSTs to /sidebar-agent/event which
dispatches through processAgentEvent. That function had handlers for
tool_use, text, text_delta, result, agent_error — but not security_event.
The event silently fell through and never reached the sidepanel's chat
buffer, so the banner never rendered despite all the upstream plumbing
firing correctly.

Caught by the new full-stack E2E test (security-e2e-fullstack.test.ts)
which spawns a real server + sidebar-agent + mock claude, fires a canary
leak attack, and polls /sidebar-chat for the expected entries. Before
this fix, the test timed out waiting for security_event to appear.

Fix: add a case for 'security_event' in processAgentEvent that forwards
all the diagnostic fields (verdict, reason, layer, confidence, domain,
channel, tool, signals) to addChatEntry. Sidepanel.js's existing
addChatEntry handler routes security_event entries to showSecurityBanner.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-04-20 05:40:54 +08:00
parent a275aa5dde
commit 2c21366cf9
+21
View File
@@ -526,6 +526,27 @@ function processAgentEvent(event: any): void {
return;
}
if (event.type === 'security_event') {
// Relay the security event as a chat entry so sidepanel.js's addChatEntry
// router (showSecurityBanner) sees it on the next /sidebar-chat poll.
// Preserve all the diagnostic fields the banner renders (verdict, reason,
// layer, confidence, domain, channel, tool).
addChatEntry({
ts,
role: 'agent',
type: 'security_event',
verdict: event.verdict,
reason: event.reason,
layer: event.layer,
confidence: event.confidence,
domain: event.domain,
channel: event.channel,
tool: event.tool,
signals: event.signals,
} as any);
return;
}
// agent_start and agent_done are handled by the caller in the endpoint handler
}