Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/components/cloud-agent-next/CloudAgentProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
type CloudAgentSessionId,
} from '@/lib/cloud-agent-sdk';
import { SESSION_INGEST_WS_URL } from '@/lib/constants';
import { usePostHog } from 'posthog-js/react';

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

Expand All @@ -24,6 +25,9 @@ type CloudAgentProviderProps = {
export function CloudAgentProvider({ children, organizationId }: CloudAgentProviderProps) {
const storeRef = useRef(createStore());
const trpcClient = useRawTRPCClient();
const posthog = usePostHog();
const posthogRef = useRef(posthog);
posthogRef.current = posthog;

// Create manager once per provider instance.
// trpcClient is stable (from context); organizationId is stable per provider mount.
Expand Down Expand Up @@ -259,6 +263,19 @@ export function CloudAgentProvider({ children, organizationId }: CloudAgentProvi
window.history.replaceState(window.history.state, '', url.toString());
}
},

onRemoteSessionOpened: ({ kiloSessionId }) => {
posthogRef.current?.capture('remote_session_opened', {
feature: 'remote-session',
kilo_session_id: kiloSessionId,
});
},
onRemoteSessionMessageSent: ({ kiloSessionId }) => {
posthogRef.current?.capture('remote_session_message_sent', {
feature: 'remote-session',
kilo_session_id: kiloSessionId,
});
},
});
}

Expand Down
19 changes: 19 additions & 0 deletions src/lib/cloud-agent-sdk/session-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type SessionConfig = {
model: string;
variant?: string | null;
};
type ActiveSessionType = 'cloud-agent' | 'cli';
type StandaloneQuestion = { requestId: string; questions: QuestionInfo[] };
type StandalonePermission = {
requestId: string;
Expand Down Expand Up @@ -95,6 +96,8 @@ type SessionManagerConfig = {
onComplete?: () => void;
onBranchChanged?: (branch: string) => void;
onSendFailed?: (messageText: string) => void;
onRemoteSessionOpened?: (data: { kiloSessionId: KiloSessionId }) => void;
onRemoteSessionMessageSent?: (data: { kiloSessionId: KiloSessionId }) => void;
};

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

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

async function switchSession(kiloSessionId: KiloSessionId): Promise<void> {
activeSessionId = kiloSessionId;
activeSessionType = null;
stateUnsub?.();
stateUnsub = null;
currentSession?.destroy();
Expand Down Expand Up @@ -500,6 +505,10 @@ function createSessionManager(config: SessionManagerConfig): SessionManager {
const ap = store.get(activePermissionAtom);
if (ap?.requestId === requestId) store.set(activePermissionAtom, null);
},
onResolved: resolved => {
if (resolved.cloudAgentSessionId) activeSessionType = 'cloud-agent';
else if (resolved.isLive) activeSessionType = 'cli';
},
onBranchChanged: branch => {
const currentFetched = store.get(fetchedSessionDataAtom);
if (currentFetched) {
Expand Down Expand Up @@ -538,6 +547,9 @@ function createSessionManager(config: SessionManagerConfig): SessionManager {
// Fallback: clear loading when events flow even if no root
// session.created was replayed (e.g. CLI snapshot failure).
store.set(isLoadingAtom, false);
if (activeSessionType === 'cli') {
config.onRemoteSessionOpened?.({ kiloSessionId });
}
},
});
session.connect();
Expand Down Expand Up @@ -567,12 +579,18 @@ function createSessionManager(config: SessionManagerConfig): SessionManager {
} satisfies StoredMessage);
try {
if (!currentSession) throw new Error('No active session');
// Snapshot before await — switchSession() can overwrite these while send is in flight.
const sessionType = activeSessionType;
const kiloSessionId = activeSessionId;
await currentSession.send({
prompt: payload.prompt,
mode: payload.mode,
model: payload.model,
variant: payload.variant,
});
if (sessionType === 'cli' && kiloSessionId) {
config.onRemoteSessionMessageSent?.({ kiloSessionId });
}
} catch (err) {
store.set(optimisticMessageAtom, null);
store.set(failedPromptAtom, payload.prompt);
Expand Down Expand Up @@ -635,6 +653,7 @@ function createSessionManager(config: SessionManagerConfig): SessionManager {
}
clearAllAtoms();
activeSessionId = null;
activeSessionType = null;
}

return {
Expand Down
2 changes: 2 additions & 0 deletions src/lib/cloud-agent-sdk/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type CloudAgentSessionConfig = {
) => void;
onPermissionResolved?: (requestId: string) => void;
onBranchChanged?: (branch: string) => void;
onResolved?: (resolved: ResolvedSession) => void;
onSessionCreated?: (info: SessionInfo) => void;
onSessionUpdated?: (info: SessionInfo) => void;
onEvent?: (event: NormalizedEvent) => void;
Expand Down Expand Up @@ -225,6 +226,7 @@ function createCloudAgentSession(config: CloudAgentSessionConfig): CloudAgentSes
if (expectedGeneration !== connectGeneration) return;

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

let factory: TransportFactory;
try {
Expand Down
Loading