Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat(site): show network calls list on AI session detail - #27426

Merged
SasSwart merged 3 commits into
mainfrom
aigov-464-frontend
Aug 4, 2026
Merged

feat(site): show network calls list on AI session detail#27426
SasSwart merged 3 commits into
mainfrom
aigov-464-frontend

Conversation

@SasSwart

@SasSwart SasSwart commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Renders the per-call network calls as a collapsible panel above the session threads, using the network_call_logs field added to the threads API.

Each row shows the request method, allowed/blocked status, URL, and timestamp, and expands to show the protocol, matched rule, and full detail. The header shows the total count and a blocked-count badge from the session summary; when the server caps the list, the panel notes how many calls are shown. The panel is omitted when the session did not pass through Agent Firewall.

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. feat: add network calls summary to AI session threads API #27417 — backend network summary (base main)
  2. feat: show network request summary on AI session detail card #27418 — frontend summary rows (base feat: add network calls summary to AI session threads API #27417)
  3. feat: add network calls list to AI session threads API #27425 — backend per-call list network_call_logs (base feat: show network request summary on AI session detail card #27418)
  4. feat(site): show network calls list on AI session detail #27426 — frontend network-calls panel (base feat: add network calls list to AI session threads API #27425)

Refs AIGOV-464

🤖 Generated with Claude Code

@linear-code

linear-code Bot commented Jul 22, 2026

Copy link
Copy Markdown

AIGOV-464

@SasSwart
SasSwart force-pushed the aigov-464-frontend branch from f9e824c to c60d5c1 Compare July 23, 2026 10:08
@SasSwart
SasSwart force-pushed the aigov-464-frontend branch from c60d5c1 to 7ad90bb Compare July 28, 2026 13:17
SasSwart added a commit that referenced this pull request Jul 30, 2026
Backend for the AI session network summary. Exposes total/blocked
network calls and top destination domains on the session threads
endpoint (`GET /api/v2/ai-gateway/sessions/{id}`).

Total and blocked reuse the existing Agent Firewall aggregation from the
sessions list query, so the numbers match the sessions table. Top
domains are a new server-side aggregation
(`GetAIBridgeSessionTopDomains`) over boundary logs, using the same
interception-window correlation. There is no network-error state,
matching the current data model.

Frontend consuming these fields is in a separate stacked PR.

### 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 (base `main`)
2. #27418 — frontend summary rows (base #27417)
3. #27425 — backend per-call list `network_call_logs` (base #27418)
4. #27426 — frontend network-calls panel (base #27425)

Refs AIGOV-463

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Co-authored-by: Cian Johnston <[email protected]>
@SasSwart
SasSwart marked this pull request as ready for review July 30, 2026 11:13
@SasSwart
SasSwart requested a review from EhabY July 30, 2026 11:13
SasSwart added a commit that referenced this pull request Jul 30, 2026
Frontend for the AI session network summary. Adds Network calls, Blocked
network requests, and Top domains rows to the Session summary card on
the individual AI session detail page, driven by the network fields on
the session threads response.

Renders "Disabled" when network monitoring was not active and "No
activity" when there were no calls. Covered by Storybook stories for
each state.

### 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-463

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Co-authored-by: Cian Johnston <[email protected]>
@SasSwart
SasSwart requested a review from a team as a code owner July 30, 2026 11:55
@EhabY

EhabY commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Can we rebase this on the latest main or something? I'm not sure what the new changes are 🙏

Base automatically changed from aigov-464 to main August 3, 2026 09:34
SasSwart added a commit that referenced this pull request Aug 3, 2026
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) <[email protected]>
@SasSwart
SasSwart force-pushed the aigov-464-frontend branch from 7ad90bb to 70a8dd2 Compare August 3, 2026 09:38
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Docs preview

Check off each page once it's been reviewed. If a page changes in a later push, its checkbox clears automatically so it gets a fresh look. Pages not yet wired into the docs navigation aren't listed here.

@coderagents

coderagents Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Documentation Check

Updates Needed

  • docs/ai-coder/ai-gateway/audit.md - The Session detail section documents the Session summary card (network requests, blocked, top domains) but not the new per-call Network calls panel this PR adds above the threads. Document that it lists individual Agent Firewall calls with method, Allowed/Blocked status, URL, and timestamp; expands to show protocol, matched rule, and full detail; shows a blocked-count badge; notes when only the first N of the total calls are shown (server cap of 1000); shows "No network calls were recorded for this session." when empty; and is omitted entirely when the session did not pass through Agent Firewall.
  • docs/images/aibridge/session_detail.png - The screenshot referenced from the Session detail section predates the network calls panel, so it no longer matches the UI.

Not flagged: docs/reference/api/aigateway.md and docs/reference/api/schemas.md are auto-generated and already regenerated in this PR for the network_call_logs field. Backend query, dbauthz, and test changes have no user-facing docs surface.


Automated review via Coder Agents

Render the per-call network calls as a collapsible panel above the session
threads. Each row shows the method, allowed/blocked status, URL, and
timestamp, and expands to show the protocol, matched rule, and full detail.
The panel is omitted when the session did not pass through Agent Firewall.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@SasSwart
SasSwart force-pushed the aigov-464-frontend branch from 70a8dd2 to d753830 Compare August 3, 2026 09:50

@EhabY EhabY left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls fix compilation and linting 🙏

Comment on lines 133 to +138
{session ? (
<SessionTimeline
initiator={session.initiator}
threads={threads}
networkCallSummary={session.network_calls}
networkCalls={session.network_call_logs ?? []}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to just pass session as a whole?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

session has quite a large footprint. I like passing only what the SessionTimeline needs as a form of self documentation. Happy to change it if you deem that better, but I think its fine as it?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could pass the session and do something like Pick<AIBridgeSessionThreadsResponse, "initiator" | "network_calls" | "network_call_logs">

I do not feel strongly but that is easier to pass IMO (if we want pass another field we just add | "new_field")

Comment thread site/src/testHelpers/entities.ts Outdated
)}
</div>

{isOpen && (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we separate this into a components for easier reading? We can avoid the ternary operators here and just have a if (...) else ...

This could actually resolve a bug where we render both when there's a summary but an empty list:

No network calls were recorded for this session.
Showing the first 0 of 4 network calls.

Please add a storybook for this actually

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a storybook for this. However, this this is not a state that is possible from the API's side? If there is no summary, there will be no calls?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah fair enough, tbh I prefer the frontend to be fully tested regardless of the backend implicit contract. I prefer this but not pressed about it

// of rendered rows.
export const BlockedBadge: Story = {
play: async ({ canvas }) => {
const header = canvas.getByText("Network calls (4)").closest("div");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe give the header a data-testid, or query the badge directly by its accessible name since this is brittle

Comment on lines +32 to +33
// The blocked-count badge in the header reflects the summary, not the number
// of rendered rows.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This claims that the badge "reflects the summary, not the number of rendered rows", but it inherits the meta args where blocked is 2 and exactly 2 rendered rows are blocked, so the story cannot tell the two apart. Give it summary: { total: 4, blocked: 9 } to actually pin the claim

await expect(canvas.queryByText("Protocol")).not.toBeInTheDocument();

const rowButtons = canvas.getAllByRole("button", { expanded: false });
// The first row button toggles the first call's detail open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unneeded comment

The API now returns firewall log entries via the shared
AgentFirewallLog type, replacing the bespoke
AIBridgeSessionNetworkCall type. Update the components and mock
data to match.
@SasSwart

SasSwart commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

@EhabY apologies. There was an unfortunate window after I merged the prior PR and before I had pushed the rebase in which you saw a bunch of irrelevant changes. That's been fixed.

I'm tending to the comments you've already made now. Will re-request new review when done.

Removes hand-rolled open/close state and CSS-driven chevron rotation
in favor of the shared Collapsible primitive, matching the pattern
used elsewhere in the codebase.
@SasSwart
SasSwart force-pushed the aigov-464-frontend branch from 9025554 to c88a182 Compare August 3, 2026 11:59
@SasSwart
SasSwart requested a review from EhabY August 3, 2026 13:32

@EhabY EhabY left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks solid now, but there are a few issues with the UX (I understand this is not a priority now, but should at least file an issue for later):

I think we should align the method/URLs into columns:
Image

We should align the items on the right here:

Image

I would expect inner items to be indented more:

Image

Comment on lines 133 to +138
{session ? (
<SessionTimeline
initiator={session.initiator}
threads={threads}
networkCallSummary={session.network_calls}
networkCalls={session.network_call_logs ?? []}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could pass the session and do something like Pick<AIBridgeSessionThreadsResponse, "initiator" | "network_calls" | "network_call_logs">

I do not feel strongly but that is easier to pass IMO (if we want pass another field we just add | "new_field")

)}
</div>

{isOpen && (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah fair enough, tbh I prefer the frontend to be fully tested regardless of the backend implicit contract. I prefer this but not pressed about it

Comment on lines +36 to +38
await expect(
canvas.getByText("Blocked network calls: 9"),
).toBeInTheDocument();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getByText("Blocked network calls: 9") never matches:
the sr-only span and the 9 are sibling nodes (NetworkCallsTable.tsx:42-46), and the matcher
reads only direct text children. This play function fails. Put the full string in one node, or
use aria-label + getByLabelText. Keep line 39.

* Drives the header count and blocked badge. Reflects the whole session, so
* its total can exceed the number of rows in `calls`, which is capped
* server-side.
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: JSDoc says summary drives the header and badge, but NetworkCallsList (:56) reuses this interface and renders neither.

Something like:

Session-wide totals. total can exceed calls.length, which is capped server-side.

created_at: "2026-03-09T09:28:19.000Z",
},
];
export const MockAIBridgeSessionNetworkCalls: readonly TypesGen.AgentFirewallLog[] =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we should probably rename this now that the type changed (MockAgentFirewallLogs)

@SasSwart
SasSwart merged commit a60f393 into main Aug 4, 2026
26 checks passed
@SasSwart
SasSwart deleted the aigov-464-frontend branch August 4, 2026 06:30
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 4, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants