Skip to content

feat(posthog-ai): support pi task sessions on the web - #108899

Merged
trunk-io[bot] merged 12 commits into
masterfrom
posthog/pi-sessions-web
Oct 13, 2026
Merged

trunk-io[bot] merged 12 commits into
masterfrom
posthog/pi-sessions-web

Conversation

@puemos

@puemos puemos commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Problem

  • A user who opens a Pi task in PostHog web sees only "Pi session logs aren't available in PostHog yet". Pi sessions work only in PostHog Desktop.
  • The Pi agent server writes its own log format and takes its own commands. The web reads ACP, the format of the Claude and Codex agent server.

Changes

With the pi-acp flag on, a Pi task on /tasks and in the inbox works like any other task:

  • The thread shows Pi messages, thoughts, tool cards, notices, errors and turn ends.
  • A user can reply, steer with "Send now", queue a follow-up, stop the run, and run the task again.
  • The composer shows every model in one list, plus the reasoning picker. It has no harness or permission mode.
  • MCP approvals and Pi extension prompts (select, confirm, input, editor) use the existing approval and question cards.
  • A resumed run shows the earlier runs of the session, also after a reload.
  • /usage and the context ring show Pi token usage. Attachments show as files.

With the flag off, the banner stays as it is today.

Demo

Recorded against the running app, with a real Pi agent and model:

Recording Shows
01 – start and tools A new task, tool cards, an edit diff, the context ring
02 – follow-up, model, queue, stop A model change, "Send now", a queued message, Stop
03 – extension prompts All four prompt kinds, a timeout, an extension failure
04 – attachment A file the agent reads
05 – reload and resume The thread after a reload, and a resumed run with its history

The links open the .webm files. GitHub shows no inline player for them.

Screenshots

Tool calls, with the edit diff and a command's output open:

Pi tool calls in the web thread

Every extension prompt kind, a timed-out prompt, and an extension failure:

Pi extension prompts answered on the web

An attachment the agent reads:

A Pi attachment in the web thread

How it works

The Pi agent server now speaks ACP, so the web reads a Pi run through its existing ACP path.

Before:

flowchart LR
    P{{Pi}} --> S[Pi agent server] --> L[(Pi-format log)]
    L --> C[Desktop]
    L -.-> W[Web: banner only]
    classDef phBlue fill:#1d4aff,stroke:#1d4aff,color:#fff;
    classDef phGray fill:#e5e7eb,stroke:#c7ccd1,color:#000;
    classDef phYellow fill:#f9bd2b,stroke:#f9bd2b,color:#000;
    class P,S phBlue;
    class L phGray;
    class C,W phYellow;
Loading

After:

flowchart LR
    P{{Pi}} --> S[Pi agent server]
    S --> F{pi-acp on?}
    F -- yes --> A[(ACP log)]
    F -- no --> L[(Pi-format log)]
    A --> W[Web run surface]
    A --> C[Desktop]
    L --> C
    L --> W
    classDef phBlue fill:#1d4aff,stroke:#1d4aff,color:#fff;
    classDef phGray fill:#e5e7eb,stroke:#c7ccd1,color:#000;
    classDef phYellow fill:#f9bd2b,stroke:#f9bd2b,color:#000;
    class P,S,F phBlue;
    class A,L phGray;
    class C,W phYellow;
Loading
  • The backend checks the flag for the run creator and tells the sandbox which format to write.
  • With ACP, the server also accepts the ACP commands the web sends: permission_response and set_config_option.
  • @posthog/agent-contracts holds the mapping between Pi events and ACP, in both directions. The Pi server, Desktop and the web all use it.
  • Desktop and the web read both formats. The web shows an old Pi-format run read-only, without its prompts.
  • Every Pi tool call now carries its tool name in _meta.posthog, so renderers never guess it.
  • A Pi run finds its earlier runs through its task session, not resume_from_run_id.

Why in the agent server: the community Pi adapters for ACP editors do the same, for example regadas/pi-acp. Translating on the web or in Django would copy the Pi semantics into a second place.

Mechanical: regenerated OpenAPI types and MCP schemas, kea typegen, and the new setting threaded through the sandbox launch path.

Note

PostHog AI chat stays closed for Pi tasks. The backend rejects them there on purpose.

Flow details

Starting a Pi run

sequenceDiagram
    participant U as Web or Desktop
    participant D as Django
    participant T as Temporal worker
    participant S as Pi agent server
    participant P as Pi
    participant L as Run log and live stream
    U->>D: POST run for a Pi task
    D->>T: start the task workflow
    T->>T: evaluate pi-acp for the run creator
    T->>S: launch with POSTHOG_PI_CONVERSATION_FORMAT acp or pi
    S->>P: start pi in RPC mode
    S->>L: run start, as _posthog/run_started or pi_run_started
    loop every Pi event
        P-->>S: RPC event
        S->>S: translate it to a conversation event
        alt format acp
            S->>S: map it with agentConversationEventToAcpNotification
        end
        S->>L: append to the log in batches, and to the live stream at once
    end
Loading

Sending a follow-up with a new model

sequenceDiagram
    participant W as Web composer
    participant D as Django command relay
    participant S as Pi agent server
    participant P as Pi
    W->>D: set_config_option model
    D->>S: forward
    S->>P: set_model
    W->>D: user_message, as a steer when the run is busy
    D->>S: forward
    S->>P: prompt or steer
    S-->>W: ACP notifications, _posthog/turn_complete, _posthog/usage_update
Loading

Answering an MCP approval or an extension prompt

sequenceDiagram
    participant P as Pi or Pi extension
    participant S as Pi agent server
    participant L as Run log and live stream
    participant W as Web
    P->>S: approval or extension_ui_request
    S->>S: keep the open prompt and start its timeout
    S->>L: _posthog/permission_request
    L-->>W: the approval or question card replaces the composer
    alt answered in time
        W->>S: permission_response
        S->>P: decision or extension_ui_response
        S->>L: _posthog/permission_resolved
    else the timeout passes first
        S->>L: _posthog/permission_resolved, and the card closes
    end
Loading
Pi source Entry written with the flag on
Conversation events session/update, or _posthog/user_message, _posthog/progress, _posthog/status, _posthog/error, _posthog/turn_complete
Run start _posthog/run_started
Turn usage _posthog/usage_update, and a usage_update session update
MCP approval or extension prompt _posthog/permission_request, then _posthog/permission_resolved
Extension notify and extension_error _posthog/status with extension_notice, or _posthog/console
Other extension UI _posthog/pi_extension_event

Every entry from a Pi extension message keeps the raw message in _meta.piExtension, so Desktop can rebuild its own extension UI.

ACP resume chain versus the Pi task session chain in the logs endpoint

How did you test this code?

Test rationale: the new tests guard the Pi-to-ACP mapping, the new commands, and the web reader. Each catches a regression no existing test covers.

  • acp-conversation.test.ts: every Pi event survives the trip to ACP and back.
  • acp-wire.test.ts: each Pi log entry and extension message maps to the right notification, and each prompt kind gets the right answer.
  • pi-agent-server.test.ts: the ACP log, approvals and prompts through permission_response, prompt timeouts, and set_config_option.
  • cloudPiSessionClient.test.ts (Desktop): both formats read the same.
  • test_api.py, test_modal_sandbox.py: Pi run fields, the session logs, the command relay, and the sandbox format setting.
  • Web: runStreamLogic, runInteractionLogic, toolResolver, composer and question tests, and RunSurfaceImpl for the flag-off banner.

End to end, on the local stack with a Docker sandbox running this branch and a real model through the LLM gateway. The recordings above come from this run. It found four bugs, all fixed here:

  • The reply text was lost after a reload.
  • The command relay rejected permission_response and set_config_option for Pi.
  • The quill thread showed an extension notice without its text.
  • A run-state write after a model change had no effect. It is removed.

Not tested:

  • A Pi sandbox on Modal.
  • Desktop against an ACP-format run. Unit tests cover its reader.
  • An MCP approval end to end. The local stack had no MCP server.
  • The flag-off banner end to end. RunSurfaceImpl.test.tsx covers it.
  • agent-server.test.ts (ACP), because the sandbox blocks git commit. The ACP server change is only the shared attachment path helper.

Known gaps, not in this PR:

  • Stop shows "Agent error: This operation was aborted" and leaves the running tool spinning. This predates this PR and affects Desktop too.
  • Pi slash commands are not in the composer menu.
  • A Pi command row reads "Terminal", because Pi bash calls carry no description.

Release status

  • This change is behind a feature flag and is not available to users

The pi-acp flag exists in PostHog. It is off, at 0%.

Warning

The flag switches the Pi server to ACP for every run its users start, also from Desktop. A Desktop build older than this PR shows those runs as empty. Roll out beyond internal users only after a Desktop release with this PR.

Desktop and backend in one PR

This PR carries desktop-skip-backend-check. Either half can ship first:

  • An agent server without this PR ignores POSTHOG_PI_CONVERSATION_FORMAT and keeps writing the Pi format.
  • A backend without this PR never sets it, so the new agent server writes the Pi format too.
  • Desktop and the web read both formats, so neither depends on the backend deploy.

Docs update

None. No existing doc covers Pi tasks on the web.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: PostHog Desktop (Claude Code), Claude Opus 5.5 (claude-opus-5-5), with one session on Claude Fable 5.1 (claude-fable-5-1)

  • The first rounds translated the Pi format in the web client. After review, the translation moved into the Pi agent server, with the mapping shared through @posthog/agent-contracts.
  • Skills: /writing-tests, /improving-drf-endpoints, /writing-ui-components, /writing-code-comments, /writing-user-facing-copy, /writing-pr-descriptions. The local CodeRabbit pass was skipped in this cloud run.
  • All story and test data is invented. The end-to-end run used a throwaway test extension that is not part of this PR.

🤖 Generated with Claude Code


Created with PostHog Desktop

@puemos puemos self-assigned this Sep 30, 2026
@trunk-io

trunk-io Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

😎 Merged successfully - details.

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Trunk lane — backend Python lane

This PR is assigned to the backend Python lane. It runs backend Python tests and may merge in parallel with PRs in other lanes.

⚠️ Complexity (TypeScript) — 62 functions above the limit (max 126)

Cyclomatic complexity above the limit in changed typescript files (10 for production files, 15 for test files). Warn only: worth simplifying when you next touch these functions.

Function Location Complexity Limit
foldLogFromCheckpoint products/posthog_ai/frontend/logics/runStreamLogic.ts:1677 126 10
ingestAcpFrame products/posthog_ai/frontend/logics/runStreamLogic.ts:4936 96 10
streamConversation frontend/src/scenes/max/maxThreadLogic.tsx:1261 73 10
onEventImplementation frontend/src/scenes/max/maxThreadLogic.tsx:3249 53 10
<anonymous> frontend/src/scenes/max/maxThreadLogic.tsx:2973 47 10
askMax frontend/src/scenes/max/maxThreadLogic.tsx:1979 45 10
sendNow products/posthog_ai/frontend/logics/runInteractionLogic.ts:1324 45 10
handleSseEvent products/posthog_ai/frontend/logics/runStreamLogic.ts:4172 43 10
getMcpToolPresentation products/posthog_ai/frontend/components/tool/GenericMcpToolRenderer.tsx:34 38 10
reconcileRunLog products/posthog_ai/frontend/logics/runStreamLogic.ts:1376 32 10
TaskRunComposer products/posthog_ai/frontend/scenes/TaskTracker/components/TaskRunComposer.tsx:48 32 10
ComposerModelEffortPickers products/posthog_ai/frontend/components/composer/ComposerModelEffortPickers.tsx:142 30 10
TaskDetailPage products/posthog_ai/frontend/scenes/TaskTracker/components/TaskDetailPage.tsx:27 30 10
reconcileHistory products/posthog_ai/frontend/logics/runStreamLogic.ts:3890 29 10
startNewRun products/posthog_ai/frontend/logics/runInteractionLogic.ts:1529 24 10
invocationFromToolCallUpdate products/posthog_ai/frontend/logics/runStreamLogic.ts:1494 24 10
dedupeBufferedAgainstHistory products/posthog_ai/frontend/logics/runStreamLogic.ts:1124 23 10
streamRun products/posthog_ai/frontend/logics/runStreamLogic.ts:4298 21 10
OptionSelector products/posthog_ai/frontend/components/OptionSelector.tsx:35 19 10
BuiltinToolRenderer products/posthog_ai/frontend/components/tool/builtinToolRenderers.tsx:467 19 10
<anonymous> frontend/src/scenes/max/maxThreadLogic.tsx:2639 18 10
enhanceThreadToolCalls frontend/src/scenes/max/maxThreadLogic.tsx:3146 18 10
MultiFieldInput products/posthog_ai/frontend/components/QuestionField.tsx:327 18 10
ExitPlanModeRenderer products/posthog_ai/frontend/components/tool/builtinToolRenderers.tsx:131 18 10
parsePermissionRequestFrame products/posthog_ai/frontend/logics/runStreamLogic.ts:871 18 10
deliverPermission products/posthog_ai/frontend/logics/runStreamLogic.ts:4569 17 10
add products/posthog_ai/frontend/logics/runStreamLogic.ts:1240 16 10
openSseForRun products/posthog_ai/frontend/logics/runStreamLogic.ts:4135 16 10
handleTerminalStatus products/posthog_ai/frontend/logics/runStreamLogic.ts:4685 16 10
coversEntry products/posthog_ai/frontend/logics/runStreamLogic.ts:1209 14 10
<anonymous> products/posthog_ai/frontend/logics/runStreamLogic.ts:3681 14 10
sseDropped products/posthog_ai/frontend/logics/runStreamLogic.ts:4428 14 10
TaskRunChat products/posthog_ai/frontend/scenes/TaskTracker/components/TaskRunChat.tsx:50 14 10
<anonymous> frontend/src/scenes/max/maxThreadLogic.tsx:3216 13 10
RunSurfaceRoot products/posthog_ai/frontend/components/RunSurfaceImpl.tsx:91 13 10
RunSurfaceComposer products/posthog_ai/frontend/components/RunSurfaceImpl.tsx:281 13 10
SubagentToolRenderer products/posthog_ai/frontend/components/tool/builtinToolRenderers.tsx:280 13 10
cancelWhenReady products/posthog_ai/frontend/logics/runCancellationLogic.ts:132 13 10
submit products/posthog_ai/frontend/logics/runInteractionLogic.ts:923 13 10
userAttachment products/posthog_ai/frontend/logics/runStreamLogic.ts:434 13 10
readLegacyPiEntry products/posthog_ai/frontend/logics/runStreamLogic.ts:502 13 10
sameFoldOptions products/posthog_ai/frontend/logics/runStreamLogic.ts:1635 13 10
bootstrapRun products/posthog_ai/frontend/logics/runStreamLogic.ts:4040 13 10
extractAgentToolName products/posthog_ai/frontend/utils/toolResolver.ts:40 13 10
extractMcpProxyCall products/posthog_ai/frontend/utils/toolResolver.ts:58 13 10
resolveToolKey products/posthog_ai/frontend/utils/toolResolver.ts:81 13 10
enqueueQueuedMessage frontend/src/scenes/max/maxThreadLogic.tsx:1841 12 10
executePendingClientToolCall frontend/src/scenes/max/maxThreadLogic.tsx:2408 12 10
<anonymous> products/posthog_ai/frontend/logics/runInteractionLogic.ts:1102 12 10
mergeToolCallUpdateEntries products/posthog_ai/frontend/logics/runStreamLogic.ts:1001 12 10
renderLiveHuman products/posthog_ai/frontend/logics/runStreamLogic.ts:1921 12 10
routePermissionRequest products/posthog_ai/frontend/logics/runStreamLogic.ts:4495 12 10
extractSandboxQuestionAnswer products/posthog_ai/frontend/policy/questionUtils.ts:133 12 10
setConversation frontend/src/scenes/max/maxThreadLogic.tsx:1718 11 10
handleKeyDown products/posthog_ai/frontend/components/OptionSelector.tsx:63 11 10
RunSurfaceThread products/posthog_ai/frontend/components/RunSurfaceImpl.tsx:214 11 10
parsePermissionOption products/posthog_ai/frontend/logics/runStreamLogic.ts:836 11 10
<anonymous> products/posthog_ai/frontend/logics/runStreamLogic.ts:1349 11 10
finalizeMessage products/posthog_ai/frontend/logics/runStreamLogic.ts:1845 11 10
pushError products/posthog_ai/frontend/logics/runStreamLogic.ts:1995 11 10
<anonymous> products/posthog_ai/frontend/logics/runStreamLogic.ts:3543 11 10
<anonymous> products/posthog_ai/frontend/logics/runStreamLogic.ts:3644 11 10
⚠️ Duplication (Python) — 5 new duplicated blocks (worst 284 tokens)

New Python code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

First copy Second copy Lines Tokens
products/tasks/backend/logic/services/agent_server_launcher.py:675 products/tasks/backend/logic/services/sandbox.py:653 41 284
products/tasks/backend/logic/services/agent_server_launcher.py:306 products/tasks/backend/logic/services/docker_sandbox.py:949 39 252
products/tasks/backend/presentation/serializers.py:3677 products/tasks/backend/presentation/serializers.py:3888 26 95
products/tasks/backend/logic/services/agent_server_launcher.py:345 products/tasks/backend/logic/services/docker_sandbox.py:992 21 86
products/tasks/backend/presentation/serializers.py:3653 products/tasks/backend/presentation/serializers.py:3867 22 82
⚠️ Duplication (TypeScript) — 1 new duplicated block (worst 172 tokens)

New TypeScript code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

First copy Second copy Lines Tokens
products/posthog_ai/frontend/components/ReadonlyRunSurfaceImpl.test.tsx:22 products/posthog_ai/frontend/components/RunSurfaceImpl.test.tsx:29 11 172
⚠️ Bundle size — 🔺 +5.9 KiB (+0.0%)

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 75.81 MiB · 🔺 +5.9 KiB (+0.0%)

File Size Δ vs base
render-query/src/render-query/render-query.js 24.38 MiB 🔺 +6.0 KiB (+0.0%)

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

✅ Eager graph — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1003.2 KiB · 23 files no change █████████░ 85.6% of 1.14 MiB
logged-out boot: index + App + bootApp (preloaded by every page, including /login)
src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
2.86 MiB · 604 files 🔺 +16 B (+0.0%) █████████░ 94.3% of 3.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.29 MiB · 2,515 files 🔺 +4.5 KiB (+0.1%) ██████████ 95.4% of 7.64 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
9.54 MiB · 3,599 files 🔺 +4.5 KiB (+0.0%) █████████░ 92.6% of 10.30 MiB
today home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
7.32 MiB · 2,525 files 🔺 +4.5 KiB (+0.1%) █████████░ 92.8% of 7.89 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
9.13 MiB · 3,434 files 🔺 +4.5 KiB (+0.0%) █████████░ 92.4% of 9.88 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
11.92 MiB · 4,276 files 🔺 +4.5 KiB (+0.0%) ████████░░ 79.2% of 15.05 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/layout/navigation-3000/navigationLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/dashboard/dashboardLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/lemon-ui/LemonMarkdown/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/RichContentEditor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/CodeSnippet/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/zod/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/billing/billingLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/ProductEmptyState/productSetupPreloadLogic.ts stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/dashboards/frontend/widgets/previews/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 zod/v4/locales/de.js stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/project-homepage/today/TodayReportPage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/activity/explore/EventsScene.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.3 KiB src/RootErrorBoundary.tsx
1.3 KiB src/index.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
839 B src/scenes/ChunkLoadErrorBoundary.tsx
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
Size File
319.8 KiB ../node_modules/.pnpm/posthog-js@1.438.8_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
103.1 KiB src/products.tsx
99.6 KiB src/lib/api.ts
69.0 KiB src/lib/lemon-ui/icons/icons.tsx
38.7 KiB ../node_modules/.pnpm/@dnd-kit+core@6.0.8_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js
33.9 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
28.6 KiB src/scenes/scenes.ts
27.7 KiB src/lib/constants.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
319.8 KiB ../node_modules/.pnpm/posthog-js@1.438.8_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.0 KiB ../packages/quill/packages/quill/dist/index.js
103.1 KiB src/products.tsx
99.6 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
Size File
319.8 KiB ../node_modules/.pnpm/posthog-js@1.438.8_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
181.9 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.0 KiB ../packages/quill/packages/quill/dist/index.js
103.1 KiB src/products.tsx
99.6 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
Size File
319.8 KiB ../node_modules/.pnpm/posthog-js@1.438.8_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.0 KiB ../packages/quill/packages/quill/dist/index.js
103.1 KiB src/products.tsx
99.6 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
Largest files eagerly shipped from src/scenes/activity/explore/EventsScene.tsx
Size File
319.8 KiB ../node_modules/.pnpm/posthog-js@1.438.8_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
181.9 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.0 KiB ../packages/quill/packages/quill/dist/index.js
103.1 KiB src/products.tsx
99.6 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Size File
319.8 KiB ../node_modules/.pnpm/posthog-js@1.438.8_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
316.0 KiB ../node_modules/.pnpm/posthog-js@1.438.8_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
181.9 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.0 KiB ../packages/quill/packages/quill/dist/index.js
103.1 KiB src/products.tsx
99.6 KiB src/lib/api.ts

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

✅ Toolbar bundle — eager 2.23 MiB within budget

What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.

Metric Size Δ vs base Budget
Eager (shipped)
entry + static imports
2.23 MiB · 19 files 🔺 +16 B (+0.0%) ████░░░░░░ 39.0% of 5.72 MiB
Deferred (lazy) 2.10 MiB · 40 files no change n/a — loads on demand
Loader dist/toolbar.js 1.2 KiB no change █░░░░░░░░░ 6.0% of 19.5 KiB
Largest eagerly-shipped chunks
Size File
873.1 KiB dist/toolbar/toolbar-app-JJ7ICAUC.css
664.0 KiB dist/toolbar/chunk-chunk-67V4GY4R.js
257.8 KiB dist/toolbar/chunk-chunk-TSK433WX.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
129.6 KiB dist/toolbar/chunk-chunk-RVAE6ENC.js
74.7 KiB dist/toolbar/toolbar-app-IFUCFPTL.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-HYUKXQTM.js
21.7 KiB dist/toolbar/chunk-chunk-2ZONIQGQ.js
6.8 KiB dist/toolbar/chunk-chunk-DV7IWQNF.js

Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile

✅ Dist folder size — 🔺 +149.2 KiB (+0.0%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1008.55 MiB · 🔺 +149.2 KiB (+0.0%)

ℹ️ MCP UI apps size — 32 app(s), 17274.7 KB JS

Built size of each MCP UI app (main.js + styles.css).

App JS CSS
debug 598.2 KB 206.2 KB
action 454.4 KB 206.2 KB
action-list 564.5 KB 206.2 KB
cohort 453.4 KB 206.2 KB
cohort-list 563.5 KB 206.2 KB
email-template 453.2 KB 206.2 KB
error-details 469.9 KB 206.2 KB
error-issue 454.8 KB 206.2 KB
error-issue-list 565.1 KB 206.2 KB
experiment 561.6 KB 206.2 KB
experiment-list 565.3 KB 206.2 KB
experiment-results 566.6 KB 206.2 KB
feature-flag 567.1 KB 206.2 KB
feature-flag-list 570.9 KB 206.2 KB
feature-flag-testing 457.6 KB 206.2 KB
inline-scan 453.9 KB 206.2 KB
insight-actors 562.7 KB 206.2 KB
llm-costs 559.6 KB 206.2 KB
session-recording 455.7 KB 206.2 KB
survey 455.0 KB 206.2 KB
survey-global-stats 562.2 KB 206.2 KB
survey-list 565.2 KB 206.2 KB
survey-stats 562.2 KB 206.2 KB
trace-span 453.8 KB 206.2 KB
trace-span-list 564.4 KB 206.2 KB
vision-observation-list 563.6 KB 206.2 KB
workflow 453.8 KB 206.2 KB
workflow-list 563.9 KB 206.2 KB
loops-review 458.1 KB 206.2 KB
query-results 816.3 KB 206.2 KB
render-ui 899.9 KB 206.2 KB
visual-review-snapshots 458.3 KB 206.2 KB
✅ Playwright — all passed

All tests passed.

View test results →

⚠️ Backend coverage — 94.0% of changed backend lines covered — 6 uncovered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ███████████████████░ 94.0% (105 / 111)

File Patch Uncovered changed lines
products/tasks/backend/temporal/process_task/activities/get_task_processing_context.py 57.1% 481–483
products/tasks/backend/presentation/serializers.py 91.7% 92, 3767
products/tasks/backend/facade/api.py 94.4% 4872

🤖 Agents: add a test only if an uncovered line exposes a realistic regression that existing tests miss. Otherwise explain why no new test is needed under "How did you test this code?". Gap list: the patch-coverage artifact on this run (gh run download 213400873286482 -n patch-coverage), or the coverage-data block at the end of this comment.

Per-product line coverage (touched products)
Product Coverage Lines
platform_features ██░░░░░░░░░░░░░░░░░░ 12.1% 7 / 58
demo ███████████░░░░░░░░░ 55.8% 1,506 / 2,699
data_tools ████████████░░░░░░░░ 61.2% 90 / 147
warehouse_sources_queue █████████████░░░░░░░ 66.8% 1,926 / 2,885
aeo ███████████████░░░░░ 76.3% 617 / 809
batch_exports ████████████████░░░░ 80.2% 21,577 / 26,893
github_commands █████████████████░░░ 83.7% 978 / 1,168
apm █████████████████░░░ 84.1% 1,306 / 1,553
engineering_analytics █████████████████░░░ 85.1% 11,410 / 13,409
mcp_analytics █████████████████░░░ 85.7% 4,849 / 5,656
ml_inference █████████████████░░░ 86.3% 524 / 607
warehouse_suggestions █████████████████░░░ 87.3% 2,252 / 2,580
posthog_ai ██████████████████░░ 87.7% 4,226 / 4,817
notebooks ██████████████████░░ 88.4% 15,242 / 17,235
today ██████████████████░░ 88.6% 2,866 / 3,233
mcp_registry ██████████████████░░ 89.0% 1,596 / 1,794
product_tours ██████████████████░░ 89.1% 1,337 / 1,500
dashboards ██████████████████░░ 89.2% 7,334 / 8,224
webmcp ██████████████████░░ 89.6% 240 / 268
cohorts ██████████████████░░ 89.6% 8,458 / 9,438
signals ██████████████████░░ 90.0% 69,696 / 77,462
data_modeling ██████████████████░░ 90.1% 10,856 / 12,043
ai_training ██████████████████░░ 90.4% 349 / 386
metrics ██████████████████░░ 90.6% 8,362 / 9,227
visual_review ██████████████████░░ 90.7% 9,853 / 10,867
data_warehouse ██████████████████░░ 90.8% 14,885 / 16,388
exports ██████████████████░░ 90.9% 9,772 / 10,751
canvas ██████████████████░░ 91.0% 7,805 / 8,581
autoresearch ██████████████████░░ 91.1% 10,436 / 11,453
business_knowledge ██████████████████░░ 91.2% 8,880 / 9,740
managed_warehouse ██████████████████░░ 91.2% 11,053 / 12,114
tasks ██████████████████░░ 91.5% 82,643 / 90,303
cdp ██████████████████░░ 91.6% 9,704 / 10,591
error_tracking ██████████████████░░ 91.6% 16,799 / 18,330
wizard ██████████████████░░ 91.7% 6,028 / 6,572
streamlit_apps ██████████████████░░ 91.9% 3,139 / 3,415
conversations ██████████████████░░ 92.1% 29,759 / 32,328
stamphog ███████████████████░ 92.5% 8,656 / 9,356
early_access_features ███████████████████░ 92.6% 1,339 / 1,446
managed_migrations ███████████████████░ 92.6% 1,630 / 1,760
alerts ███████████████████░ 92.9% 7,246 / 7,797
review_hog ███████████████████░ 92.9% 16,541 / 17,798
surveys ███████████████████░ 93.1% 6,626 / 7,118
web_analytics ███████████████████░ 93.1% 24,961 / 26,811
notifications ███████████████████░ 93.2% 1,152 / 1,236
approvals ███████████████████░ 93.4% 4,526 / 4,848
cross_project_dashboards ███████████████████░ 93.4% 880 / 942
slack_app ███████████████████░ 93.5% 14,855 / 15,883
context_layer ███████████████████░ 93.7% 3,409 / 3,640
marketing_analytics ███████████████████░ 93.8% 20,074 / 21,402
messaging ███████████████████░ 93.8% 4,468 / 4,762
billing_alerts ███████████████████░ 93.9% 2,090 / 2,226
mcp_store ███████████████████░ 93.9% 9,008 / 9,593
customer_analytics ███████████████████░ 93.9% 26,130 / 27,821
workflows ███████████████████░ 93.9% 17,808 / 18,957
experiments ███████████████████░ 94.1% 34,286 / 36,432
ai_observability ███████████████████░ 94.1% 26,476 / 28,126
legal_documents ███████████████████░ 94.3% 2,289 / 2,427
replay_vision ███████████████████░ 94.3% 30,115 / 31,928
logs ███████████████████░ 94.5% 15,850 / 16,770
actions ███████████████████░ 94.6% 973 / 1,029
reminders ███████████████████░ 94.8% 760 / 802
endpoints ███████████████████░ 94.8% 9,333 / 9,847
skills ███████████████████░ 94.8% 7,063 / 7,447
growth ███████████████████░ 94.8% 11,268 / 11,880
annotations ███████████████████░ 95.0% 816 / 859
tracing ███████████████████░ 95.2% 3,631 / 3,815
data_catalog ███████████████████░ 95.5% 4,420 / 4,628
access_control ███████████████████░ 95.6% 7,881 / 8,245
product_analytics ███████████████████░ 95.8% 28,751 / 30,027
alerts_platform ███████████████████░ 95.8% 5,012 / 5,234
revenue_analytics ███████████████████░ 95.9% 1,878 / 1,959
data_quality ███████████████████░ 95.9% 8,569 / 8,933
feature_flags ███████████████████░ 96.2% 26,932 / 27,997
warehouse_sources ███████████████████░ 96.3% 438,276 / 455,218
security ███████████████████░ 96.7% 1,452 / 1,501
pulse ███████████████████░ 97.4% 2,023 / 2,078
analytics_platform ████████████████████ 98.0% 2,775 / 2,833
groups ████████████████████ 98.0% 1,818 / 1,855
field_notes ████████████████████ 99.4% 172 / 173

Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.

✅ Hogbox preview — ready, open the preview

▶ Open the preview

🔑 Login test@posthog.com / 12345678 (demo data)
🧩 Running this PR's backend and frontend, on the PostHog :master base
🔗 Link stable across rebuilds: a re-push swaps the box underneath, the URL stays
🔒 Access tailnet only (PostHog VPN)
🛠️ Admin inspect & debug state in hogland
💤 Idle sleeps after ~30 min idle (snapshot to S3, zero node cost) and wakes on your next visit in ~30s, behind a brief "waking up" screen

commit 7054b27 · box box-c2de2e8079e9 · ready in 1295s (push → usable) · build log · rebuilds on every push, torn down on close

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

The change adds Pi runtime support to task APIs, task surfaces, and stream processing. It translates Pi history and live events into stored notifications, handles Pi permission requests, and reconciles covered event IDs. Pi runs use session history and Pi-specific model and effort settings. The change also updates retry and compaction status displays, supports extension-question input fields, and adds task fixtures and tests.

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to fb255

The Pi web sessions feature is behind a flag and the remaining issues are edge cases: a stale runtime marker, a late state response, attachment-footer parsing, and duplicate usage counts on history rebuilds. They are worth fixing, but the change is mergeable once the owner is aware of them.

Security architecture risk: 🟡 Moderate · up to fb255

The change adds consequential browser-based approval and task-session controls. Existing task access checks limit exposure, but approval failure handling and authorization of live configuration changes could not be verified end to end.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The reviewed history change broadens readable content within an already accessible task session, not across tasks or teams. The predecessor query is bounded and requires the same active session. Commands can affect that task's sandbox and available tools; external credential and tool authority were not fully inspected.

Trust Boundaries and Controls

  • observed — Browser-controlled commands pass through authenticated task access checks. The backend rejects Pi commands for non-Pi tasks, applies code-access checks to consequential Pi commands subject to existing exemptions, validates the sandbox URL, and attaches connection credentials obtained for the selected task run.
  • observed — Frontend approval delivery requires a matching source run, a nonterminal current run, and a live rather than replay-only surface. Delivery keys combine run and request identity, suppress duplicate in-flight sends, and support cancellation. These client controls supplement rather than replace backend authority.

Resilience and Maintainability Implications

  • observed — The backend distinguishes a terminal run whose sandbox is gone from transient sandbox readiness: the former receives a definitive run-ended response. Frontend approval delivery preserves a retry path for delivery errors and separately handles definitively ended targets. Whether Pi inner-command rejection follows this same failure contract remains unresolved.

Hardening Proposals

  • proposed — Validate the deployed Pi approval acknowledgement contract and interrupted live-configuration transitions, including inner-command rejection. Confirm that unsuccessful decisions remain actionable and that live configuration changes enforce the same entitlement boundary as run creation. This is a verification proposal, not an observed vulnerability.
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is complete and self-contained. It includes the problem, user-visible changes, screenshots, flow diagrams, test rationale, automated and end-to-end validation, known gaps, release stat…
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (2)
products/posthog_ai/frontend/logics/runStreamLogic.ts-2926-2933 (1)

2926-2933: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear piRuntime on reset.

runStreamLogic is keyed by streamKey, so a stable instance can be reused. RunSurfaceBootstrap resets the instance before bootstrap, and taskRuntime can be omitted. bootstrapRun then preserves the previous piRuntime value. Because omitted task runtime defaults to ACP, an ACP permission request can use pi/rpc instead of permission_response.

retryConnection and attachOptimisticResume do not call reset; they retain runtime state for same-task retries and resumes.

Suggested fix
         piRuntime: [
             false,
             {
                 bootstrapRun: (state, { taskRuntime }) =>
                     taskRuntime === undefined ? state : taskRuntime === TaskRuntimeEnumApi.Pi,
                 markPiRuntime: () => true,
+                reset: () => false,
             },
         ],
products/posthog_ai/frontend/logics/runStreamLogic.ts-3809-3816 (1)

3809-3816: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Normalize persisted Pi permission requests.

normalizeHistory preserves _posthog/permission_request options unchanged. An active, unresolved replay restores the pending permission card, but parsePermissionRequestFrame does not add allow_once. A persisted Pi MCP request that contains only allow_always can therefore omit the one-shot action.

Pass piRuntime to parsePermissionRequestFrame and apply the existing withPiOneShotAllow rule at that parsing boundary. The live buffered-path claim is too broad because live frames are normalized before buffering when piRuntime is already true.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: PostHog/posthog/.coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: e1ebaf04-6f26-4fc0-90e3-80ef06f9712a

📥 Commits

Reviewing files that changed from the base of the PR and between 1c66a26 and 042de15.

📒 Files selected for processing (14)
  • products/posthog_ai/frontend/components/RunSurfaceImpl.test.tsx
  • products/posthog_ai/frontend/components/RunSurfaceImpl.tsx
  • products/posthog_ai/frontend/components/ThreadItems.tsx
  • products/posthog_ai/frontend/logics/runInteractionLogic.test.ts
  • products/posthog_ai/frontend/logics/runInteractionLogic.ts
  • products/posthog_ai/frontend/logics/runStreamLogic.test.ts
  • products/posthog_ai/frontend/logics/runStreamLogic.ts
  • products/posthog_ai/frontend/scenes/TaskTracker/TaskTracker.stories.tsx
  • products/posthog_ai/frontend/scenes/TaskTracker/components/TaskDetailPage.tsx
  • products/posthog_ai/frontend/scenes/TaskTracker/components/TaskRunChat.tsx
  • products/posthog_ai/frontend/scenes/TaskTracker/components/TaskRunComposer.tsx
  • products/posthog_ai/frontend/types/wireTypes.ts
  • products/posthog_ai/frontend/utils/piWire.test.ts
  • products/posthog_ai/frontend/utils/piWire.ts

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 7 remain after this review.

@trunk-io

trunk-io Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

Failed Test Failure Summary Logs
Scenes-App/SidePanels SidePanelNotebooks smoke-test The test timed out while waiting for loading indicators or spinners to disappear. Logs ↗︎
Metrics/MetricsDashboardImportModal ImportProgress smoke-test The page timed out waiting for loading indicators to disappear; a spinner remained visible. Logs ↗︎
Scenes-App/Project Homepage ProjectHomepage smoke-test The test timed out while waiting for loading indicators or spinners to disappear. Logs ↗︎
Scenes-App/OAuth/Authorize DefaultScopes smoke-test The test timed out while waiting for loading indicators or spinners to disappear. Logs ↗︎

... and 1 more

View Full Report ↗︎ ⋅ Docs

@coderabbitai coderabbitai Bot 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.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (1)
products/posthog_ai/frontend/utils/piWire.ts-331-331 (1)

331-331: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Validate permission options before calling startsWith.

isPermissionRequestFrame checks only the top-level type. If an option is null or its kind is missing or not a string, withPiMcpOptions throws before parsePermissionOption can reject it. The exception escapes handleSseEvent and can interrupt stream processing.

Filter invalid options before this transformation.

Suggested fix
-    const options = (Array.isArray(frame.options) ? frame.options : []).map((option) =>
-        option.kind.startsWith('reject') ? { ...option, _meta: { ...option._meta, hint: PI_MCP_REJECT_HINT } } : option
-    )
+    const options = (Array.isArray(frame.options) ? frame.options : [])
+        .filter((option): option is PermissionOption => typeof option?.kind === 'string')
+        .map((option) =>
+            option.kind.startsWith('reject') ? { ...option, _meta: { ...option._meta, hint: PI_MCP_REJECT_HINT } } : option
+        )

Source: Learnings


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: PostHog/posthog/.coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: 6125fd75-04ab-494b-83a2-916c6094c07d

📥 Commits

Reviewing files that changed from the base of the PR and between b1738e7 and c94a4ea.

📒 Files selected for processing (11)
  • products/posthog_ai/frontend/components/OptionSelector.tsx
  • products/posthog_ai/frontend/components/QuestionField.tsx
  • products/posthog_ai/frontend/components/QuestionInput.test.tsx
  • products/posthog_ai/frontend/components/QuestionInput.tsx
  • products/posthog_ai/frontend/components/ThreadItems.tsx
  • products/posthog_ai/frontend/logics/runStreamLogic.test.ts
  • products/posthog_ai/frontend/logics/runStreamLogic.ts
  • products/posthog_ai/frontend/policy/questionUtils.ts
  • products/posthog_ai/frontend/types/wireTypes.ts
  • products/posthog_ai/frontend/utils/piWire.test.ts
  • products/posthog_ai/frontend/utils/piWire.ts

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 6 remain after this review.

@puemos puemos changed the title feat(posthog-ai): support pi task sessions in the web run surface feat(posthog-ai): support pi task sessions on the web behind a flag Sep 30, 2026

@coderabbitai coderabbitai Bot 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.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (4)
products/tasks/backend/facade/api.py-8909-8912 (1)

8909-8912: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve Pi effort selections during default resolution.

run_task applies _with_ai_run_defaults to fresh runs. That helper calls apply_ai_run_defaults, which resolves effort-only selections as ACP because it does not pass the request runtime. An ACP preference can therefore add runtime_adapter and model; the reviewed branch then removes both model and the explicitly requested reasoning_effort.

Make default resolution runtime-aware. Do not apply ACP defaults to Pi requests, and preserve the requested reasoning_effort.

products/posthog_ai/frontend/logics/runStreamLogic.ts-4685-4685 (1)

4685-4685: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep idless turn usage stable across history rebuilds.

When a turn has no event_id, each ingestion creates a new map key. Reconnect and finalization rebuilds replay those turns, but cache.piTurnUsage survives the rebuild. The same turn therefore increases token totals again.

Rebuild the usage map from the reconciled log, or assign stable per-entry keys before replay.

products/posthog_ai/frontend/utils/piWire.ts-158-158 (1)

158-158: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve user-written file lists.

A prompt ending with Attached files:\n- README.md matches this pattern without any uploaded attachment. piAttachedFiles then removes that text and creates an attachment chip instead. The replay no longer shows the original prompt.

Only strip the footer when its paths match the server-generated attachment layout and UUID-prefixed filenames. Otherwise, preserve the text.

products/posthog_ai/frontend/logics/runInteractionLogic.ts-1485-1513 (1)

1485-1513: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Ignore delayed get_state results after disposal or a run change.

markRunStarted dispatches setPiSessionConfig after its request without checking that the interaction logic is still current. The active-creation flow changes the logic key when it switches to the created run, so the old logic can be disposed while its request is still pending. The late response can then dispatch against that disposed logic.

Do not reset cache.piSessionConfigRunId on failure. The documented fallback is once per run and does not require retrying the request.

Suggested fix
                 cache.piSessionConfigRunId = props.runId
+                const runId = props.runId
+                const disposables = cache.disposables
                 try {
                     const response = await tasksRunsCommandCreate(
                         String(currentProjectId),
                         props.taskId,
-                        props.runId,
+                        runId,
                         piRpcRequest({ type: 'get_state' }, uuid())
                     )
+                    if (disposables.isDisposed || props.runId !== runId) {
+                        return
+                    }
                     const config = parsePiSessionConfig(response.result)
                     if (config) {
                         actions.setPiSessionConfig(config)
                     }
                 } catch {
-                    // The picker keeps its fallback. A send with a pick still applies it.
+                    // The picker keeps its fallback. A send with a pick still applies it.
                 }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: PostHog/posthog/.coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: 48089def-2098-4b3d-98e7-3e70cb26863e

📥 Commits

Reviewing files that changed from the base of the PR and between c94a4ea and eff3c0b.

⛔ Files ignored due to path filters (4)
  • products/logs/frontend/generated/api.ts is excluded by !**/generated/**
  • products/tasks/frontend/generated/api.schemas.ts is excluded by !**/generated/**
  • products/tasks/frontend/generated/api.zod.ts is excluded by !**/generated/**
  • services/mcp/src/generated/tasks/api.ts is excluded by !**/generated/**
📒 Files selected for processing (20)
  • frontend/src/lib/constants.tsx
  • products/posthog_ai/frontend/components/RunSurfaceImpl.test.tsx
  • products/posthog_ai/frontend/components/RunSurfaceImpl.tsx
  • products/posthog_ai/frontend/components/composer/ComposerModelEffortPickers.test.tsx
  • products/posthog_ai/frontend/components/composer/ComposerModelEffortPickers.tsx
  • products/posthog_ai/frontend/logics/runInteractionLogic.test.ts
  • products/posthog_ai/frontend/logics/runInteractionLogic.ts
  • products/posthog_ai/frontend/logics/runStreamLogic.test.ts
  • products/posthog_ai/frontend/logics/runStreamLogic.ts
  • products/posthog_ai/frontend/scenes/TaskTracker/components/TaskDetailPage.tsx
  • products/posthog_ai/frontend/scenes/TaskTracker/components/TaskRunComposer.tsx
  • products/posthog_ai/frontend/utils/composerModels.ts
  • products/posthog_ai/frontend/utils/piWire.test.ts
  • products/posthog_ai/frontend/utils/piWire.ts
  • products/tasks/backend/facade/api.py
  • products/tasks/backend/models.py
  • products/tasks/backend/presentation/serializers.py
  • products/tasks/backend/presentation/views/api.py
  • products/tasks/backend/tests/test_api.py
  • services/mcp/src/api/generated.ts

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 7 remain after this review.

@puemos
puemos force-pushed the posthog/pi-sessions-web branch from eff3c0b to fb255bd Compare September 30, 2026 15:13

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟠 Major · Guard the Pi state response against context replacement. · runInteractionLogic.ts:1508

products/posthog_ai/frontend/logics/runInteractionLogic.ts:1508
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard the Pi state response against context replacement.

If the Kea context closes while get_state is in flight, the response still calls setPiSessionConfig on the old logic. Capture getContext() before the request and compare it before dispatching the result. Also confirm that the project, task, and run still match the request.

As per coding guidelines: “An in-flight request resolving … still needs its own guard: compare getContext() against the context the resource was set up in.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: PostHog/posthog/.coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: 057ac39e-4078-4271-9665-b88411dc0ae4

📥 Commits

Reviewing files that changed from the base of the PR and between eff3c0b and fb255bd.

⛔ Files ignored due to path filters (4)
  • products/logs/frontend/generated/api.ts is excluded by !**/generated/**
  • products/tasks/frontend/generated/api.schemas.ts is excluded by !**/generated/**
  • products/tasks/frontend/generated/api.zod.ts is excluded by !**/generated/**
  • services/mcp/src/generated/tasks/api.ts is excluded by !**/generated/**
📒 Files selected for processing (7)
  • frontend/src/lib/constants.tsx
  • products/posthog_ai/frontend/logics/runInteractionLogic.ts
  • products/tasks/backend/facade/api.py
  • products/tasks/backend/presentation/serializers.py
  • products/tasks/backend/presentation/views/api.py
  • products/tasks/backend/tests/test_api.py
  • services/mcp/src/api/generated.ts

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 6 remain after this review.

@posthog

posthog Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

✅ Visual changes approved by @puemos — baseline updated in 9185f41.

View this run in PostHog

2 new.

Install the Visual Review Chrome extension to see visual review results at the top of your pull requests.

@puemos
puemos force-pushed the posthog/pi-sessions-web branch from 9185f41 to 2ada4b8 Compare October 1, 2026 18:17
@puemos
puemos requested a review from jonathanlab October 1, 2026 18:58
@puemos puemos changed the title feat(posthog-ai): support pi task sessions on the web behind a flag feat(posthog-ai): support pi task sessions on the web Oct 2, 2026
@puemos
puemos force-pushed the posthog/pi-sessions-web branch from 2ada4b8 to 2becf58 Compare October 2, 2026 09:11

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

Thanks for this!

Could we ensure that there is as much code sharing happening as possible instead of duplicating the code? It would be great if we could keep the Pi logic surface as small as possible

Comment thread frontend/src/lib/constants.tsx Outdated
PHAI_SANDBOX_MODE: 'phai-sandbox-mode', // owner: #team-posthog-ai
PHAI_SCENE_AUTO_OPEN: 'phai-scene-auto-open', // owner: #team-posthog-ai
PHAI_TASKS: 'phai-tasks', // owner: #team-array
PI_WEB_SESSIONS: 'pi-web-sessions', // owner: #team-ai-observability, shows Pi task runs on the web run surface

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.

Owned by #team-ai-observability? Shouldnt it be your team..?

onOpenDefaultSettings?: () => void
/** Who pays for a run on the Codex harness. Shown only while Codex is selected; omit to hide the row. */
codexBilling?: ComposerCodexBilling
singleHarness?: boolean

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.

What does singleHarness imply here? Difficult to read. Seems like we should just properly adjust modelsForRuntimeAdapter to handle the pi runtime

help_text="Reasoning effort to request for models that expose an effort control.",
help_text=(
"Reasoning effort to request for models that expose an effort control. "
"A Pi task sets it as the Pi thinking level and does not accept 'ultracode'."

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.

A Pi task sets it as the Pi thinking level and does not accept 'ultracode
This is not a useful addition, ultracode is something we do not properly support in general AFAIK. That's just claude code's xhigh + a prompt nudge to use orchestration.

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.

A lot of this code seems duplicated from agent/src/pi/conversation/ and the like.

@puemos
puemos force-pushed the posthog/pi-sessions-web branch from bae2734 to 2166c5d Compare October 7, 2026 14:03
@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 7054b27.

@puemos puemos added the desktop-skip-backend-check Skip the check that blocks desktop and backend changes in one PR label Oct 8, 2026 — with PostHog

puemos commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor Author

Changes since the review

Shared code

  • Before: the web had its own Pi translation (piWire.ts).
  • After: @posthog/agent-contracts holds the Pi and ACP mapping. The Pi server, Desktop and the web use it.

Pi server

  • Before: writes only the Pi format.
  • After: writes ACP when pi-acp is on. Accepts permission_response and set_config_option.

Backend

  • Before: sends no format setting.
  • After: checks the flag for each run and tells the sandbox the format.

Web

  • Before: translates Pi entries and sends pi/rpc.
  • After: reads Pi runs through the ACP path. A small reader shows old Pi-format runs.

Desktop

  • Before: reads only the Pi format.
  • After: reads the two formats.

Test

  • Before: unit tests only.
  • After: an end-to-end test with a real model, and five videos in the PR text. The test found four bugs. This PR fixes them.

CI is green. The desktop-skip-backend-check label is on this PR, because the two halves can ship in any order.

@puemos
puemos marked this pull request as ready for review October 13, 2026 11:29
@parameterai

parameterai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Risk: No findings

This PR makes Pi task sessions work on the web by having the Pi agent server speak ACP. Since the last review the branch was only rebased onto a newer master: the PR's own security-relevant code (ACP wire mapping, command relay allowlist, serializers, sandbox env, run stream logic) is byte-identical to the reviewed state, and the only PR-authored edits are an error_type passthrough line in the agent server and a disabled-propagation UI hardening fix. No new security-relevant behavior was introduced by the delta.

Sentinel reviewed 7054b27 · Review settings

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested review from a team October 13, 2026 11:30
puemos and others added 3 commits October 13, 2026 13:45
Pi runs skip ACP run defaults, so an explicit Pi effort is kept. The Pi MCP one-shot allow applies to live and persisted requests. Reset clears the Pi runtime, a history rebuild resets Pi usage, a user-written file list stays text, and a late get_state response is ignored. Removes comments the PR added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Pi adapter fills only the shared _meta.posthog tool contract, so the web renderers decide titles and inputs for Pi tool calls the same way as for ACP calls. The model picker takes the task runtime instead of a singleHarness flag. The Pi ultracode rule and help text go, because the Pi server runs ultracode at xhigh. Both run serializers share one Pi validation helper, and AI run defaults resolve by task runtime so a Pi task takes a stored Pi default.

Generated-By: PostHog Desktop
Task-Id: 9ec0ad97-81e1-4a94-acb2-a4cf310c196e
createPiToolCallRecord sets _meta.posthog (harness-neutral tool names and mcpProxy), the Pi server writes attachments to the shared .posthog/attachments layout and records them as resource_link blocks, and the web adapter copies structured fields instead of inferring names or parsing an "Attached files:" footer. The composer model and effort selectors read one session baseline, and extension notices carry a message field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 9ec0ad97-81e1-4a94-acb2-a4cf310c196e
… helpers

The attachment artifactId fallback and run id back-fill only served the removed "Attached files:" text parser; server-written resource links already carry the run and artifact through the attachment layout. The get_state, set_model and set_thinking_level builders each wrapped a one-line literal, and several piWire and contract symbols had no importer outside their module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 9ec0ad97-81e1-4a94-acb2-a4cf310c196e
… web

agentConversationEventToSessionUpdate and agentConversationEventToAcpNotification move into @posthog/agent-contracts. Desktop's conversation builder and the web's Pi adapter both call them instead of keeping parallel copies. The web reaches the module through a source path alias, the same way it consumes @posthog/llm-normalizer, so no build step or install is added. A subagent tool update now keeps its PostHog tool metadata alongside the parent id, where Desktop previously replaced it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 9ec0ad97-81e1-4a94-acb2-a4cf310c196e
No consumer imports the session update or notification types from the package index; the web imports the module subpath and Desktop uses only the mapping function.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 9ec0ad97-81e1-4a94-acb2-a4cf310c196e
With the flag on for the run's creator, the backend sets POSTHOG_PI_CONVERSATION_FORMAT=acp and the Pi agent server converts its log and stream entries to ACP notifications at the boundary. It also accepts permission_response and set_config_option, closes timed-out extension prompts, and reports usage through _posthog/usage_update.

The web reads Pi runs through its ACP path, so utils/piWire.ts is deleted; old pi_event and pi_run_started entries still read through the shared mapping. Desktop's cloud Pi client reads both formats through the new reverse mapping in @posthog/agent-contracts, and raw extension messages ride in _meta.piExtension.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 9ec0ad97-81e1-4a94-acb2-a4cf310c196e
Found in a local end-to-end run of Pi on the web:

- The task run log drops agent_message_chunk entries, so the ACP-format Pi log lost the reply text after a reload. The Pi server now joins consecutive reply chunks into one agent_message with its first and last event ids, holding a still-streaming message back until it completes.
- The command relay rejected permission_response and set_config_option for Pi tasks, so the web could not answer Pi prompts or change the model mid-run.
- The quill thread showed an extension notice as a generic "Extension notice" divider without its message.
- Removed the run-state model write after set_config_option: model and reasoning_effort are protected run state keys, so it never took effect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 9ec0ad97-81e1-4a94-acb2-a4cf310c196e
Formats the files Biome flagged, sorts the inline kea typegen the way CI regenerates it, and tightens the Pi log flush: the carry keeps its entry type and is put back in one place, and dialog timers use optional handles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 9ec0ad97-81e1-4a94-acb2-a4cf310c196e
The flag switches the Pi agent server to ACP and shows Pi runs on the web, so the name now tells the main change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 9ec0ad97-81e1-4a94-acb2-a4cf310c196e
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 9ec0ad97-81e1-4a94-acb2-a4cf310c196e
@puemos
puemos force-pushed the posthog/pi-sessions-web branch from e8be872 to 7054b27 Compare October 13, 2026 11:47
@trunk-io
trunk-io Bot merged commit dcef940 into master Oct 13, 2026
334 of 338 checks passed
@trunk-io
trunk-io Bot deleted the posthog/pi-sessions-web branch October 13, 2026 15:17
@deployment-status-posthog

deployment-status-posthog Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-10-13 17:21 UTC Run
prod-us ✅ Deployed 2026-10-13 17:38 UTC Run
prod-eu ✅ Deployed 2026-10-13 17:38 UTC Run

This branch was successfully deployed

1 active deployment
preview-pr-108899 — 7054b275 Deployed Oct 13, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

desktop-skip-backend-check Skip the check that blocks desktop and backend changes in one PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants