Skip to content

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

Draft
raquelmsmith wants to merge 6 commits into
mainfrom
posthog-code/show-posthog-resources-used
Draft

feat(agent): show PostHog products used below each turn#2476
raquelmsmith wants to merge 6 commits into
mainfrom
posthog-code/show-posthog-resources-used

Conversation

@raquelmsmith
Copy link
Copy Markdown
Member

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

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
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.

1 participant