Skip to content

feat(agent): show PostHog products used below each turn#2476

Merged
raquelmsmith merged 11 commits into
mainfrom
posthog-code/show-posthog-resources-used
Jun 5, 2026
Merged

feat(agent): show PostHog products used below each turn#2476
raquelmsmith merged 11 commits into
mainfrom
posthog-code/show-posthog-resources-used

Conversation

@raquelmsmith
Copy link
Copy Markdown
Member

@raquelmsmith raquelmsmith commented Jun 3, 2026

When the agent uses a PostHog resource via the MCP exec dispatcher during a turn, surface a chip row under that turn's final message listing the products touched (Experiments, Feature flags, SQL, Error tracking, …). A turn with no PostHog calls shows no row.

Detection and classification live in the agent (main/business layer); the renderer is strictly UI:

  • packages/agent/src/posthog-products.ts: classify an exec sub-tool's domain into a stable product id (admin/meta domains hidden; unknown → generic "PostHog"). Exported via @posthog/agent.
  • PostToolUse hook records the product behind each executed call onto a per-turn Set on the session; reset at prompt start.
  • At turn end the agent emits a new _posthog/resources_used notification with the turn's products, then clears the accumulator.
  • Renderer: buildConversationItems handles the notification and places a resources_used item under the final message; ResourcesUsedView renders the chips.

Tests: posthog-products classification, and buildConversationItems placement / empty-payload handling.

image

Generated-By: PostHog Code
Task-Id: f2ff4d75-f51e-4618-9e64-68ca4be237fa

Problem

Changes

How did you test this?

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

When the agent uses a PostHog resource via the MCP `exec` dispatcher during a
turn, surface a chip row under that turn's final message listing the products
touched (Experiments, Feature flags, SQL, Error tracking, …). A turn with no
PostHog calls shows no row.

Detection and classification live in the agent (main/business layer); the
renderer is strictly UI:

- packages/agent/src/posthog-products.ts: classify an exec sub-tool's domain
  into a stable product id (admin/meta domains hidden; unknown → generic
  "PostHog"). Exported via @posthog/agent.
- PostToolUse hook records the product behind each executed `call` onto a
  per-turn Set on the session; reset at prompt start.
- At turn end the agent emits a new `_posthog/resources_used` notification with
  the turn's products, then clears the accumulator.
- Renderer: buildConversationItems handles the notification and places a
  `resources_used` item under the final message; ResourcesUsedView renders the
  chips.

Tests: posthog-products classification, and buildConversationItems placement /
empty-payload handling.

Generated-By: PostHog Code
Task-Id: f2ff4d75-f51e-4618-9e64-68ca4be237fa
Adds [resources_used]-tagged debug logging across the full chain so we can
see where the per-turn PostHog products row breaks:

- PostToolUse hook (hooks.ts): logs the SDK's actual tool_name for any
  posthog tool, whether isPostHogExecTool matched, and the extracted
  sub-tool — catches a tool-name/regex mismatch.
- claude-agent.ts: logs each classified resource as it's recorded, and
  whether emitResourcesUsed fires + what it emits.
- buildConversationItems.ts (renderer): logs any resource notification the
  builder receives and whether the RESOURCES_USED branch matched.

Agent logs land in the dev logs; renderer logs in chromium.log. Temporary
diagnostic — to be reverted once the root cause is found.

Generated-By: PostHog Code
Task-Id: f2ff4d75-f51e-4618-9e64-68ca4be237fa
A turn that counts feature flags via `SELECT count() FROM feature_flags`
was tagged only "SQL" — the classifier saw the execute-sql sub-tool and
had no visibility into what the query was about. The chip reflected the
mechanism, not the product the user asked about.

Now, for execute-sql, the raw command (which embeds the HogQL) is threaded
through the PostToolUse hook to a new classifier that extracts FROM/JOIN
tables and maps them to products (feature_flags -> Feature flags,
experiments -> Experiments, events/persons -> Product analytics,
session_replay_events -> Session replay, surveys, logs). A query touching
no mapped table falls back to the generic "SQL" chip, so nothing vanishes.

- posthog-products.ts: TABLE_PRODUCT map, extractSqlTables, classifyPostHogSqlQuery,
  classifyPostHogExecCall (returns 0..n products; SQL can touch several tables).
- hooks.ts / options.ts: onPostHogResourceUsed now also receives the raw command.
- claude-agent.ts: accumulates all products from classifyPostHogExecCall.
- Exported the new helpers; added classifyPostHogSqlQuery / classifyPostHogExecCall tests.

Generated-By: PostHog Code
Task-Id: f2ff4d75-f51e-4618-9e64-68ca4be237fa
SQL table → product attribution is an exact-name match, but taking the
last dotted segment meant a schema-qualified warehouse table like
`stripe.feature_flags` would wrongly map to Feature flags. Now a qualified
reference only maps when its schema is a known PostHog schema (`system`);
any other prefix (a warehouse source) is left unmapped.

Exact matching also means similarly-named tables such as
`statsig_feature_flags` or `feature_flags_archive` never match a product —
they fall back to the generic SQL chip. Added tests covering both cases.

Generated-By: PostHog Code
Task-Id: f2ff4d75-f51e-4618-9e64-68ca4be237fa
…omposer

Reworks the resources-used display from a per-turn inline row into a
persistent bar above the chat composer that fills in live as resources are
used and shows each product only once for the whole session.

Agent:
- Session accumulator renamed turnResources -> sessionResources; it now
  lives for the whole session and is never reset between turns.
- Detection emits `_posthog/resources_used` immediately for each newly-seen
  product (session-wide dedup), instead of buffering one emit at turn end.
  Removed the two turn-end emit sites and the per-prompt reset.

Renderer:
- New SessionResourcesBar (mirrors PlanStatusBar) rendered above the
  composer in SessionView. accumulateSessionResources derives the deduped,
  first-seen-ordered product list from the session's events, so it works for
  both live streaming and log replay.
- Removed the inline conversation-item path: buildConversationItems no longer
  emits a resources_used item, the SessionUpdateView case/type are gone, and
  ResourcesUsedView is deleted.

Tests updated/added: accumulateSessionResources (dedup, ordering, empty),
buildConversationItems no longer renders an inline item, agent session-field
rename.

Generated-By: PostHog Code
Task-Id: f2ff4d75-f51e-4618-9e64-68ca4be237fa
…rder

The resources bar sat in a full-width `bg-gray-2` band with a top border,
which read as a heavy separate strip. Remove both so it inherits the chat
area's background and sits flush above the composer.

Generated-By: PostHog Code
Task-Id: f2ff4d75-f51e-4618-9e64-68ca4be237fa
Each resource chip now links to its product's page on posthog.com (e.g.
Feature flags → /docs/feature-flags), opened via openUrlInBrowser
(os.openExternal). Doc URLs verified against posthog.com/docs. The map is
Partial — products without a dedicated docs page (apm) stay non-clickable
rather than linking somewhere misleading. Clickable chips get a pointer
cursor, hover state, and a title tooltip.

Also adds bottom margin (mb-3) so the bar sits less tightly against the
composer.

Generated-By: PostHog Code
Task-Id: f2ff4d75-f51e-4618-9e64-68ca4be237fa
Strips the temporary [resources_used]-tagged debug logging now that the
feature is verified: the PostToolUse hook's posthog-tool log and its logger
param, and the two debug logs in createOnPostHogResourceUsed / emitResourcesUsed.
Detection, classification, and emission are unchanged.

Generated-By: PostHog Code
Task-Id: f2ff4d75-f51e-4618-9e64-68ca4be237fa
- Fix stale comment on onPostHogResourceUsed (it accumulates session-wide and
  deduped, not a per-turn summary).
- Trim @posthog/agent index to only re-export the PostHogProductId type; the
  classifier functions and POSTHOG_PRODUCTS const aren't used outside the
  package (claude-agent.ts imports them via the relative path), so the
  re-exports were dead public-API surface.

Generated-By: PostHog Code
Task-Id: f110e589-9df9-40fc-9fff-7ff10356aca6
@raquelmsmith raquelmsmith marked this pull request as ready for review June 4, 2026 16:40
@raquelmsmith raquelmsmith requested a review from a team June 4, 2026 16:40
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 4, 2026

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
packages/agent/src/posthog-products.test.ts:10-61
**Prefer parameterised tests here**

Several test cases pack multiple independent assertions into a single `it()` block rather than using `it.each`. When one assertion fails it silences the rest, making failures harder to diagnose. The three most obvious candidates:

- `"maps resource sub-tools to their product"` (lines 10–29): 10 input/output pairs
- `"classifies query-* sub-tools by query type"` (lines 31–48): 8 pairs
- `"returns null for admin/meta/introspection domains"` (lines 56–61): 4 pairs

The same pattern appears in `classifyPostHogSqlQuery` (e.g. `"attributes a query to the product behind its tables"` has 3 pairs; `"does not match a warehouse table…"` has 2). Each of these could be an `it.each` table.

Reviews (1): Last reviewed commit: "Merge branch 'main' into posthog-code/sh..." | Re-trigger Greptile

Comment thread packages/agent/src/posthog-products.test.ts Outdated
Adds a "Code" chip (links to posthog.com/code) to the session resources bar
and surfaces it whenever the agent reads a file from the codebase:

- posthog-products.ts: new `code` product.
- PostToolUse hook: fires `onCodeFileRead` on any `Read` tool use; the agent
  records the `code` product through the same session-wide dedup + emit path
  as MCP resources. Detection lives in the hook (not the enrichment hook) so
  it isn't gated on PostHog instrumentation being present in the file.
- SessionResourcesBar.tsx: CodeIcon + docs URL for the chip.

Fixes the failing unit-test suite: SessionResourcesBar.test.ts imported the
component, which transitively pulls in the tRPC client (via openUrlInBrowser)
and throws "Could not find electronTRPC global" under vitest. Extracted the
pure `accumulateSessionResources` into its own dependency-free module
(mirroring buildConversationItems.ts) and pointed the renamed test at it.

Addresses the review comment: parameterised the multi-assertion blocks in
posthog-products.test.ts with `it.each` so a single failing case no longer
masks the rest.

Generated-By: PostHog Code
Task-Id: f110e589-9df9-40fc-9fff-7ff10356aca6
Copy link
Copy Markdown
Member

@charlesvien charlesvien left a comment

Choose a reason for hiding this comment

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

LGTM, works great

@raquelmsmith raquelmsmith merged commit 5a638ea into main Jun 5, 2026
18 checks passed
@raquelmsmith raquelmsmith deleted the posthog-code/show-posthog-resources-used branch June 5, 2026 01:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants