PostHog / PostHog/ai-plugin

Capture Claude Code sessions when `SessionEnd` doesn't fire (orchestrated / sandboxed runs)

Open
#77 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
84
Forks
15
Avg merge
14h 34m
Merged PRs (30d)
7

Description

Capture Claude Code sessions when SessionEnd doesn't fire (orchestrated / sandboxed runs)

Hi 👋 — thanks for the plugin, LLMA capture has been really useful and the code is a pleasure to read through.

We're running Claude Code inside ephemeral Daytona sandboxes via sandbox-agent, which launches Claude through the claude-agent-acp adapter. In that setup the LLMA SessionEnd hook never fires — the sandbox is destroyed when the task completes (effectively SIGKILL), and the documented SessionEnd matchers (clear | resume | logout | prompt_input_exit | bypass_permissions_disabled | other, per the hooks docs) don't obviously cover an ACP session closing either. Result: zero capture, silently — session-end-llma.py does sys.exit(0) on any error / early return (a very reasonable "never break the user's workflow" choice!), so it's easy to think it's working when it isn't.

We confirmed the sender itself is fine: running the same JSONL through ${CLAUDE_PLUGIN_ROOT}/hooks/session-end-llma.py manually (piping {"session_id": ..., "cwd": ...} on stdin) cleanly delivers full capture — ~160 events in our case, generations + tool spans + traces, parser handles the still-growing JSONL gracefully. So the data is on disk and the path works; there just isn't an orchestrator-friendly way to trigger it.

Proposal: an opt-in Stop-hook flush mode

What would unblock us — and feels like a good default for autonomous / orchestrated agent setups generally — is an optional incremental-flush mode gated by an env var:

POSTHOG_LLMA_FLUSH_ON=session_end   # current behavior, default
POSTHOG_LLMA_FLUSH_ON=stop          # flush on every Stop hook (per turn)

This pairs naturally with POSTHOG_LLMA_TRACE_GROUPING=message: each user prompt → one trace → one flush boundary, which gives a clean de-dup story (only emit events whose prompt_id matches the just-finished turn). The orchestrator survives the host killing the process between turns without losing anything.

A doc-only alternative would also help our specific case — just blessing "pipe {session_id, cwd} into hooks/session-end-llma.py from your orchestrator" in the README — though it leaves the silent-failure mode in place for everyone else.

Happy to send a PR; mostly wanted to surface the case and hear how you'd prefer to shape it.

Environment
  • Plugin posthog v1.1.21 via claude-plugins-official (source PostHog/ai-plugin@ff08c376)
  • Claude Code on macOS for local testing; Daytona sandbox via claude-agent-acp for the orchestrated path
  • PostHog Cloud, EU region

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with hooks/session-end-llma.py and the Claude Code hook configuration, then trace how SessionEnd currently supplies session_id and cwd. Check the Stop-hook behavior and the POSTHOG_LLMA_FLUSH_ON and POSTHOG_LLMA_TRACE_GROUPING options; done means an opt-in stop mode flushes completed turns while session_end remains the default.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
ai, tooling
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.