feat: add network calls list to AI session threads API (#27425)

The AI session threads API returned only a network call *summary*
(total/blocked counts + top domains). This adds the per-call list so the
session detail can render individual Agent Firewall network calls.

`ListAIBridgeSessionNetworkCalls` reuses the same sequence-number
windowing as the existing summary and includes all protocols. The list
is exposed as `network_call_logs` on the threads response and is capped
server-side at 100 rows. The summary (`network_calls.total`/`blocked`)
remains authoritative for whole-session totals: the list length and its
blocked count equal the summary only when a session has at most 100
calls, and are truncated beyond that.

### PR map (merge strictly bottom-up)

This change is a 4-PR stack. Each PR depends on all the ones below it,
so merge in this exact order:

1. #27417 — backend network summary
2. #27418 — frontend summary rows
3. #27425 — backend per-call list `network_call_logs`
4. #27426 — frontend network-calls panel

Refs AIGOV-464

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sas Swart
2026-08-03 11:34:27 +02:00
committed by GitHub
co-authored by Claude Opus 4.8
parent fba9f0d485
commit 8886a5749a
17 changed files with 432 additions and 61 deletions
+39
View File
@@ -618,6 +618,45 @@ FROM domains
ORDER BY count DESC, domain ASC
LIMIT COALESCE(NULLIF(@limit_::integer, 0), 5);
-- name: ListAIBridgeSessionNetworkCalls :many
-- Returns the individual Agent Firewall network calls made during an AI
-- session, ordered chronologically. All protocols are included, unlike
-- GetAIBridgeSessionTopDomains which considers only HTTP egress, so the list
-- covers the same events the network_calls summary in ListAIBridgeSessions
-- counts. The list is capped at @limit_ rows, so its length equals the summary
-- total only for sessions at or below the cap. The summary stays authoritative
-- for whole-session totals.
--
-- Windowing mirrors that summary and GetAIBridgeSessionTopDomains: each
-- interception's boundary logs fall in the open interval (this seq, next
-- interception's seq) within the same firewall session. The exclusive lower
-- bound drops the interception's own LLM-provider call. next_seq considers all
-- interceptions in the firewall session so windows never bleed across AI
-- sessions that share one firewall session, and falls back to the maximum
-- sequence_number for the last interception so the window stays an
-- index-satisfiable range.
SELECT bl.*
FROM aibridge_interceptions afi
LEFT JOIN LATERAL (
SELECT COALESCE(MIN(nxt.agent_firewall_sequence_number), 2147483647) AS next_seq
FROM aibridge_interceptions nxt
WHERE nxt.agent_firewall_session_id = afi.agent_firewall_session_id
AND nxt.agent_firewall_sequence_number > afi.agent_firewall_sequence_number
) w ON true
JOIN boundary_logs bl
ON bl.session_id = afi.agent_firewall_session_id
AND bl.sequence_number > afi.agent_firewall_sequence_number
AND bl.sequence_number < w.next_seq
WHERE afi.session_id = @session_id::text
AND afi.ended_at IS NOT NULL
AND afi.agent_firewall_session_id IS NOT NULL
AND afi.agent_firewall_sequence_number IS NOT NULL
-- created_at leads because a session can span several firewall sessions, whose
-- sequence numbers are independent streams. id breaks remaining ties so the row
-- that lands on the limit boundary is stable across identical requests.
ORDER BY bl.created_at ASC, bl.sequence_number ASC, bl.id ASC
LIMIT COALESCE(NULLIF(@limit_::integer, 0), 1000);
-- name: ListAIBridgeSessionThreads :many
-- Returns all interceptions belonging to paginated threads within a session.
-- Threads are paginated by (started_at, thread_id) cursor.