Skip to content

Commit e847e79

Browse files
authored
feat(cloud-agent): add PostHog tracking for remote session events (#1767)
Add onResolved callback to CloudAgentSession that fires after transport resolution, giving the session manager access to the authoritative ResolvedSession. Use this to track an ActiveSessionType ('cloud-agent' | 'cli') and emit PostHog analytics events (remote_session_opened, remote_session_message_sent) for live CLI sessions.
1 parent f639f79 commit e847e79

3 files changed

Lines changed: 38 additions & 0 deletions

File tree

src/components/cloud-agent-next/CloudAgentProvider.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
type CloudAgentSessionId,
1414
} from '@/lib/cloud-agent-sdk';
1515
import { SESSION_INGEST_WS_URL } from '@/lib/constants';
16+
import { usePostHog } from 'posthog-js/react';
1617

1718
const ManagerContext = createContext<SessionManager | null>(null);
1819

@@ -24,6 +25,9 @@ type CloudAgentProviderProps = {
2425
export function CloudAgentProvider({ children, organizationId }: CloudAgentProviderProps) {
2526
const storeRef = useRef(createStore());
2627
const trpcClient = useRawTRPCClient();
28+
const posthog = usePostHog();
29+
const posthogRef = useRef(posthog);
30+
posthogRef.current = posthog;
2731

2832
// Create manager once per provider instance.
2933
// trpcClient is stable (from context); organizationId is stable per provider mount.
@@ -259,6 +263,19 @@ export function CloudAgentProvider({ children, organizationId }: CloudAgentProvi
259263
window.history.replaceState(window.history.state, '', url.toString());
260264
}
261265
},
266+
267+
onRemoteSessionOpened: ({ kiloSessionId }) => {
268+
posthogRef.current?.capture('remote_session_opened', {
269+
feature: 'remote-session',
270+
kilo_session_id: kiloSessionId,
271+
});
272+
},
273+
onRemoteSessionMessageSent: ({ kiloSessionId }) => {
274+
posthogRef.current?.capture('remote_session_message_sent', {
275+
feature: 'remote-session',
276+
kilo_session_id: kiloSessionId,
277+
});
278+
},
262279
});
263280
}
264281

src/lib/cloud-agent-sdk/session-manager.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type SessionConfig = {
4040
model: string;
4141
variant?: string | null;
4242
};
43+
type ActiveSessionType = 'cloud-agent' | 'cli';
4344
type StandaloneQuestion = { requestId: string; questions: QuestionInfo[] };
4445
type StandalonePermission = {
4546
requestId: string;
@@ -95,6 +96,8 @@ type SessionManagerConfig = {
9596
onComplete?: () => void;
9697
onBranchChanged?: (branch: string) => void;
9798
onSendFailed?: (messageText: string) => void;
99+
onRemoteSessionOpened?: (data: { kiloSessionId: KiloSessionId }) => void;
100+
onRemoteSessionMessageSent?: (data: { kiloSessionId: KiloSessionId }) => void;
98101
};
99102

100103
// Writable/read-only atom aliases for the public atoms record
@@ -299,6 +302,7 @@ function createSessionManager(config: SessionManagerConfig): SessionManager {
299302
// Private mutable state
300303
let activeSessionId: KiloSessionId | null = null;
301304
let currentSession: CloudAgentSession | null = null;
305+
let activeSessionType: ActiveSessionType | null = null;
302306
let stateUnsub: (() => void) | null = null;
303307
let indicatorTimer: ReturnType<typeof setTimeout> | null = null;
304308

@@ -418,6 +422,7 @@ function createSessionManager(config: SessionManagerConfig): SessionManager {
418422

419423
async function switchSession(kiloSessionId: KiloSessionId): Promise<void> {
420424
activeSessionId = kiloSessionId;
425+
activeSessionType = null;
421426
stateUnsub?.();
422427
stateUnsub = null;
423428
currentSession?.destroy();
@@ -500,6 +505,10 @@ function createSessionManager(config: SessionManagerConfig): SessionManager {
500505
const ap = store.get(activePermissionAtom);
501506
if (ap?.requestId === requestId) store.set(activePermissionAtom, null);
502507
},
508+
onResolved: resolved => {
509+
if (resolved.cloudAgentSessionId) activeSessionType = 'cloud-agent';
510+
else if (resolved.isLive) activeSessionType = 'cli';
511+
},
503512
onBranchChanged: branch => {
504513
const currentFetched = store.get(fetchedSessionDataAtom);
505514
if (currentFetched) {
@@ -538,6 +547,9 @@ function createSessionManager(config: SessionManagerConfig): SessionManager {
538547
// Fallback: clear loading when events flow even if no root
539548
// session.created was replayed (e.g. CLI snapshot failure).
540549
store.set(isLoadingAtom, false);
550+
if (activeSessionType === 'cli') {
551+
config.onRemoteSessionOpened?.({ kiloSessionId });
552+
}
541553
},
542554
});
543555
session.connect();
@@ -567,12 +579,18 @@ function createSessionManager(config: SessionManagerConfig): SessionManager {
567579
} satisfies StoredMessage);
568580
try {
569581
if (!currentSession) throw new Error('No active session');
582+
// Snapshot before await — switchSession() can overwrite these while send is in flight.
583+
const sessionType = activeSessionType;
584+
const kiloSessionId = activeSessionId;
570585
await currentSession.send({
571586
prompt: payload.prompt,
572587
mode: payload.mode,
573588
model: payload.model,
574589
variant: payload.variant,
575590
});
591+
if (sessionType === 'cli' && kiloSessionId) {
592+
config.onRemoteSessionMessageSent?.({ kiloSessionId });
593+
}
576594
} catch (err) {
577595
store.set(optimisticMessageAtom, null);
578596
store.set(failedPromptAtom, payload.prompt);
@@ -635,6 +653,7 @@ function createSessionManager(config: SessionManagerConfig): SessionManager {
635653
}
636654
clearAllAtoms();
637655
activeSessionId = null;
656+
activeSessionType = null;
638657
}
639658

640659
return {

src/lib/cloud-agent-sdk/session.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type CloudAgentSessionConfig = {
4242
) => void;
4343
onPermissionResolved?: (requestId: string) => void;
4444
onBranchChanged?: (branch: string) => void;
45+
onResolved?: (resolved: ResolvedSession) => void;
4546
onSessionCreated?: (info: SessionInfo) => void;
4647
onSessionUpdated?: (info: SessionInfo) => void;
4748
onEvent?: (event: NormalizedEvent) => void;
@@ -225,6 +226,7 @@ function createCloudAgentSession(config: CloudAgentSessionConfig): CloudAgentSes
225226
if (expectedGeneration !== connectGeneration) return;
226227

227228
console.log('[cli-debug] resolveAndConnect: resolved=%o', resolved);
229+
config.onResolved?.(resolved);
228230

229231
let factory: TransportFactory;
230232
try {

0 commit comments

Comments
 (0)