diff --git a/cloud-agent-next/src/execution/orchestrator.ts b/cloud-agent-next/src/execution/orchestrator.ts index f8116c6ff..dcb21511e 100644 --- a/cloud-agent-next/src/execution/orchestrator.ts +++ b/cloud-agent-next/src/execution/orchestrator.ts @@ -322,7 +322,6 @@ export class ExecutionOrchestrator { setupCommands: initContext.setupCommands, mcpServers: initContext.mcpServers, botId: initContext.botId, - skipLinking: true, githubAppType: initContext.githubAppType, // Note: existingMetadata requires CloudAgentSessionState, not our simplified type ...gitSource, diff --git a/cloud-agent-next/src/persistence/CloudAgentSession.ts b/cloud-agent-next/src/persistence/CloudAgentSession.ts index d2d529341..d602d0537 100644 --- a/cloud-agent-next/src/persistence/CloudAgentSession.ts +++ b/cloud-agent-next/src/persistence/CloudAgentSession.ts @@ -225,7 +225,6 @@ export class CloudAgentSession extends DurableObject { // Create DO context for the ingest handler to call back into the DO const doContext: IngestDOContext = { updateKiloSessionId: (id: string) => this.updateKiloSessionId(id), - linkKiloSessionInBackend: (id: string) => this.linkKiloSessionInBackend(id), updateUpstreamBranch: (branch: string) => this.updateUpstreamBranch(branch), clearActiveExecution: () => this.clearActiveExecution(), getExecution: async (executionId: string) => { @@ -574,41 +573,6 @@ export class CloudAgentSession extends DurableObject { await this.updateMetadata(updated); } - /** - * Link the kiloSessionId to the backend for analytics/tracking. - * Called when a session_created event is received from the CLI. - * - * @param kiloSessionId - The kilo CLI session ID to link - */ - async linkKiloSessionInBackend(kiloSessionId: string): Promise { - const metadata = await this.getMetadata(); - if (!metadata?.kilocodeToken) { - throw new Error('Cannot link session: missing kilocodeToken'); - } - - const backendUrl = (this.env as unknown as WorkerEnv).KILOCODE_BACKEND_BASE_URL; - if (!backendUrl) { - throw new Error('Cannot link session: KILOCODE_BACKEND_BASE_URL not configured'); - } - - const response = await fetch(`${backendUrl}/api/cloud-sessions/linkSessions`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${metadata.kilocodeToken}`, - }, - body: JSON.stringify({ - cloudSessionId: this.sessionId, - kiloSessionId: kiloSessionId, - }), - }); - - if (!response.ok) { - const text = await response.text(); - throw new Error(`Backend link failed: ${response.status} ${text}`); - } - } - // --------------------------------------------------------------------------- // Wrapper Communication Methods // --------------------------------------------------------------------------- diff --git a/cloud-agent-next/src/persistence/types.ts b/cloud-agent-next/src/persistence/types.ts index 069cc0714..46a76dad7 100644 --- a/cloud-agent-next/src/persistence/types.ts +++ b/cloud-agent-next/src/persistence/types.ts @@ -4,6 +4,7 @@ import type { CloudAgentSession } from './CloudAgentSession.js'; import type { EncryptedSecrets } from '../router/schemas.js'; import type { CallbackTarget } from '../callbacks/index.js'; import type { Images } from './schemas.js'; +import type { SessionIngestBinding } from '../session-ingest-binding.js'; /** * Base configuration shared by all MCP server types @@ -170,7 +171,7 @@ export type PersistenceEnv = { /** Durable Object namespace for CloudAgentSession metadata (SQLite-backed) with RPC support */ CLOUD_AGENT_SESSION: DurableObjectNamespace; /** Service binding for the session ingest worker */ - SESSION_INGEST: Fetcher; + SESSION_INGEST: SessionIngestBinding; /** Shared secret for JWT token validation */ NEXTAUTH_SECRET: string; /** Comma-separated list of allowed Origins for /stream WebSocket connections */ diff --git a/cloud-agent-next/src/router/handlers/session-prepare.ts b/cloud-agent-next/src/router/handlers/session-prepare.ts index b144103e2..19a2053a1 100644 --- a/cloud-agent-next/src/router/handlers/session-prepare.ts +++ b/cloud-agent-next/src/router/handlers/session-prepare.ts @@ -6,6 +6,7 @@ import { SessionService, determineBranchName, runSetupCommands, + writeAuthFile, writeMCPSettings, } from '../../session-service.js'; import { InstallationLookupService } from '../../services/installation-lookup-service.js'; @@ -190,35 +191,6 @@ const prepareSessionHandler = internalApiProtectedProcedure } } - // NOTE: Backend session creation (createKiloSessionInBackend) is temporarily disabled. - // The kiloSessionId will now come from the kilo CLI server's POST /session API. - // This can be re-enabled later if backend analytics/tracking is needed. - // const gitUrlForBackend = input.githubRepo - // ? `https://github.com/${input.githubRepo}` - // : input.gitUrl; - // let backendKiloSessionId: string; - // try { - // backendKiloSessionId = await sessionService.createKiloSessionInBackend( - // cloudAgentSessionId, - // ctx.authToken, - // ctx.env, - // input.kilocodeOrganizationId, - // input.mode, - // input.model, - // gitUrlForBackend - // ); - // } catch (error) { - // logger - // .withFields({ error: error instanceof Error ? error.message : String(error) }) - // .error('Failed to create cliSession in backend'); - // throw new TRPCError({ - // code: 'INTERNAL_SERVER_ERROR', - // message: `Failed to create session in backend: ${ - // error instanceof Error ? error.message : String(error) - // }`, - // }); - // } - // 3. Get sandbox logger.info('Getting sandbox'); const sandbox = getSandbox(ctx.env.Sandbox, sandboxId, { sleepAfter: 900 }); @@ -310,6 +282,9 @@ const prepareSessionHandler = internalApiProtectedProcedure await writeMCPSettings(sandbox, sessionHome, input.mcpServers); } + // 9b. Write auth file for session ingest + await writeAuthFile(sandbox, sessionHome, ctx.authToken); + // 10. Start kilo server logger.info('Starting kilo server'); const kiloServerPort = await ensureKiloServer( @@ -327,52 +302,98 @@ const prepareSessionHandler = internalApiProtectedProcedure logger.setTags({ kiloSessionId }); logger.info('Created kilo CLI session'); - // 12. Get DO stub and store metadata + // 12. Create cli_sessions_v2 record via session-ingest RPC (blocking) + logger.info('Creating cli_sessions_v2 record via session-ingest'); + try { + await sessionService.createCliSessionViaSessionIngest( + kiloSessionId, + cloudAgentSessionId, + ctx.userId, + ctx.env, + input.kilocodeOrganizationId, + 'cloud-agent' + ); + } catch (error) { + logger + .withFields({ error: error instanceof Error ? error.message : String(error) }) + .error('Failed to create cli_sessions_v2 record'); + throw new TRPCError({ + code: 'INTERNAL_SERVER_ERROR', + message: `Failed to create session record: ${ + error instanceof Error ? error.message : String(error) + }`, + }); + } + + const rollbackCliSession = async () => { + await sessionService + .deleteCliSessionViaSessionIngest(kiloSessionId, ctx.userId, ctx.env) + .catch((rollbackError: unknown) => { + logger + .withFields({ + error: + rollbackError instanceof Error ? rollbackError.message : String(rollbackError), + }) + .error('Failed to rollback cli_sessions_v2 record'); + }); + }; + + // 13. Get DO stub and store metadata const doId = ctx.env.CLOUD_AGENT_SESSION.idFromName(`${ctx.userId}:${cloudAgentSessionId}`); const stub = ctx.env.CLOUD_AGENT_SESSION.get(doId); - const prepareResult = await stub.prepare({ - sessionId: cloudAgentSessionId, - userId: ctx.userId, - orgId: input.kilocodeOrganizationId, - botId: ctx.botId, - kiloSessionId, - prompt: input.prompt, - mode: input.mode, - model: input.model, - kilocodeToken: ctx.authToken, - githubRepo: input.githubRepo, - githubToken: input.githubToken, - githubInstallationId: resolvedInstallationId, - githubAppType: resolvedGithubAppType, - gitUrl: input.gitUrl, - gitToken: input.gitToken, - envVars: input.envVars, - encryptedSecrets: input.encryptedSecrets, - setupCommands: input.setupCommands, - mcpServers: input.mcpServers, - upstreamBranch: input.upstreamBranch, - autoCommit: input.autoCommit, - condenseOnComplete: input.condenseOnComplete, - appendSystemPrompt: input.appendSystemPrompt, - callbackTarget: input.callbackTarget, - images: input.images, - // Workspace metadata - workspacePath, - sessionHome, - branchName, - sandboxId, - }); + let prepareResult; + try { + prepareResult = await stub.prepare({ + sessionId: cloudAgentSessionId, + userId: ctx.userId, + orgId: input.kilocodeOrganizationId, + botId: ctx.botId, + kiloSessionId, + prompt: input.prompt, + mode: input.mode, + model: input.model, + kilocodeToken: ctx.authToken, + githubRepo: input.githubRepo, + githubToken: input.githubToken, + githubInstallationId: resolvedInstallationId, + githubAppType: resolvedGithubAppType, + gitUrl: input.gitUrl, + gitToken: input.gitToken, + envVars: input.envVars, + encryptedSecrets: input.encryptedSecrets, + setupCommands: input.setupCommands, + mcpServers: input.mcpServers, + upstreamBranch: input.upstreamBranch, + autoCommit: input.autoCommit, + condenseOnComplete: input.condenseOnComplete, + appendSystemPrompt: input.appendSystemPrompt, + callbackTarget: input.callbackTarget, + images: input.images, + // Workspace metadata + workspacePath, + sessionHome, + branchName, + sandboxId, + }); + } catch (error) { + logger + .withFields({ error: error instanceof Error ? error.message : String(error) }) + .error('DO prepare() threw, rolling back cli_sessions_v2 record'); + await rollbackCliSession(); + throw error; + } if (!prepareResult.success) { logger.withFields({ error: prepareResult.error }).error('Failed to prepare session in DO'); + await rollbackCliSession(); throw new TRPCError({ code: 'BAD_REQUEST', message: prepareResult.error ?? 'Failed to prepare session', }); } - // 13. Record kilo server activity for idle timeout tracking + // 14. Record kilo server activity for idle timeout tracking try { await withDORetry( () => ctx.env.CLOUD_AGENT_SESSION.get(doId), @@ -388,7 +409,7 @@ const prepareSessionHandler = internalApiProtectedProcedure logger.info('Session prepared successfully'); - // 14. Return both IDs + // 15. Return both IDs return { cloudAgentSessionId, kiloSessionId }; }); }); diff --git a/cloud-agent-next/src/session-ingest-binding.ts b/cloud-agent-next/src/session-ingest-binding.ts new file mode 100644 index 000000000..5a5ff04cf --- /dev/null +++ b/cloud-agent-next/src/session-ingest-binding.ts @@ -0,0 +1,33 @@ +/** + * RPC method types for the SESSION_INGEST service binding. + * + * `wrangler types` only sees `Fetcher` for service bindings; the actual RPC + * shape comes from the session-ingest worker's WorkerEntrypoint and is + * declared here so the generated file can be freely regenerated. + * + * Keep in sync with: cloudflare-session-ingest/src/session-ingest-rpc.ts + */ + +export type CreateSessionForCloudAgentParams = { + sessionId: string; + kiloUserId: string; + cloudAgentSessionId: string; + organizationId?: string; + createdOnPlatform: string; +}; + +export type DeleteSessionForCloudAgentParams = { + sessionId: string; + kiloUserId: string; +}; + +export type ExportSessionParams = { + sessionId: string; + kiloUserId: string; +}; + +export type SessionIngestBinding = Fetcher & { + createSessionForCloudAgent(params: CreateSessionForCloudAgentParams): Promise; + deleteSessionForCloudAgent(params: DeleteSessionForCloudAgentParams): Promise; + exportSession(params: ExportSessionParams): Promise; +}; diff --git a/cloud-agent-next/src/session-prepare.test.ts b/cloud-agent-next/src/session-prepare.test.ts index e2c487ff8..8ef2ec6e2 100644 --- a/cloud-agent-next/src/session-prepare.test.ts +++ b/cloud-agent-next/src/session-prepare.test.ts @@ -53,14 +53,15 @@ vi.mock('./kilo/server-manager.js', () => ({ // Define mocks BEFORE vi.mock() to avoid hoisting issues // vi.hoisted() ensures these are available when the mock factory runs -const { generateSessionIdMock, createKiloSessionInBackendMock, deleteKiloSessionInBackendMock } = - vi.hoisted(() => ({ - generateSessionIdMock: vi.fn(() => 'agent_12345678-1234-1234-1234-123456789abc'), - createKiloSessionInBackendMock: vi - .fn() - .mockResolvedValue('123e4567-e89b-12d3-a456-426614174000'), - deleteKiloSessionInBackendMock: vi.fn().mockResolvedValue(undefined), - })); +const { + generateSessionIdMock, + createCliSessionViaSessionIngestMock, + deleteCliSessionViaSessionIngestMock, +} = vi.hoisted(() => ({ + generateSessionIdMock: vi.fn(() => 'agent_12345678-1234-1234-1234-123456789abc'), + createCliSessionViaSessionIngestMock: vi.fn().mockResolvedValue(undefined), + deleteCliSessionViaSessionIngestMock: vi.fn().mockResolvedValue(undefined), +})); // Mock session-service to isolate router tests vi.mock('./session-service.js', () => ({ @@ -70,6 +71,7 @@ vi.mock('./session-service.js', () => ({ (sessionId: string, upstreamBranch?: string) => upstreamBranch || `session/${sessionId}` ), runSetupCommands: vi.fn().mockResolvedValue(undefined), + writeAuthFile: vi.fn().mockResolvedValue(undefined), writeMCPSettings: vi.fn().mockResolvedValue(undefined), InvalidSessionMetadataError: class InvalidSessionMetadataError extends Error { constructor( @@ -82,8 +84,8 @@ vi.mock('./session-service.js', () => ({ } }, SessionService: class SessionService { - createKiloSessionInBackend = createKiloSessionInBackendMock; - deleteKiloSessionInBackend = deleteKiloSessionInBackendMock; + createCliSessionViaSessionIngest = createCliSessionViaSessionIngestMock; + deleteCliSessionViaSessionIngest = deleteCliSessionViaSessionIngestMock; getOrCreateSession = vi.fn().mockResolvedValue(createMockExecutionSession()); buildContext = vi.fn().mockReturnValue({ sandboxId: 'test-sandbox', @@ -174,6 +176,11 @@ function createInternalApiContext(options: { idFromName: vi.fn((id: string) => ({ id })), get: vi.fn(() => doStub), } as unknown as TRPCContext['env']['CLOUD_AGENT_SESSION'], + SESSION_INGEST: { + fetch: vi.fn(), + createSessionForCloudAgent: vi.fn().mockResolvedValue(undefined), + deleteSessionForCloudAgent: vi.fn().mockResolvedValue(undefined), + } as unknown as TRPCContext['env']['SESSION_INGEST'], INTERNAL_API_SECRET: effectiveInternalApiSecret, NEXTAUTH_SECRET: 'test-secret', }, @@ -184,8 +191,8 @@ describe('prepareSession endpoint', () => { beforeEach(() => { vi.clearAllMocks(); generateSessionIdMock.mockReturnValue('agent_12345678-1234-1234-1234-123456789abc'); - createKiloSessionInBackendMock.mockResolvedValue('123e4567-e89b-12d3-a456-426614174000'); - deleteKiloSessionInBackendMock.mockResolvedValue(undefined); + createCliSessionViaSessionIngestMock.mockResolvedValue(undefined); + deleteCliSessionViaSessionIngestMock.mockResolvedValue(undefined); }); describe('authentication', () => { @@ -426,7 +433,7 @@ describe('prepareSession endpoint', () => { ).rejects.toThrow('Session already prepared'); }); - // NOTE: Backend session creation (createKiloSessionInBackend) is currently disabled. + // NOTE: CLI session creation (createCliSessionViaSessionIngest) is handled via session-ingest. // The kiloSessionId now comes from the kilo CLI server's POST /session API. // Tests for backend session creation error handling and rollback have been removed. }); diff --git a/cloud-agent-next/src/session-service.test.ts b/cloud-agent-next/src/session-service.test.ts index a83610b23..128478c1a 100644 --- a/cloud-agent-next/src/session-service.test.ts +++ b/cloud-agent-next/src/session-service.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; vi.mock('@cloudflare/sandbox', () => ({ getSandbox: vi.fn(), @@ -46,11 +46,9 @@ import type { PersistenceEnv, CloudAgentSessionState } from './persistence/types describe('SessionService', () => { beforeEach(() => { vi.clearAllMocks(); - mockEnv.SESSION_INGEST.fetch = vi.fn().mockResolvedValue({ - ok: true, - status: 200, - text: vi.fn().mockResolvedValue(JSON.stringify({ info: {}, messages: [] })), - }) as unknown as PersistenceEnv['SESSION_INGEST']['fetch']; + mockEnv.SESSION_INGEST.exportSession = vi + .fn() + .mockResolvedValue(JSON.stringify({ info: {}, messages: [] })); }); const mockedSetupWorkspace = vi.mocked(mockSetupWorkspace); @@ -74,7 +72,7 @@ describe('SessionService', () => { } as unknown as PersistenceEnv['CLOUD_AGENT_SESSION'], NEXTAUTH_SECRET: 'mock-secret', SESSION_INGEST: { - fetch: vi.fn(), + exportSession: vi.fn(), } as unknown as PersistenceEnv['SESSION_INGEST'], }; @@ -117,6 +115,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_test_123'; mockedSetupWorkspace.mockResolvedValue({ @@ -170,17 +170,14 @@ describe('SessionService', () => { expect(result.streamKilocodeExec).toBeDefined(); }); - it('does not restore session snapshot during initiate (no fetch)', async () => { - const payload = JSON.stringify({ info: {}, messages: [] }); - const fetchMock = vi.fn().mockResolvedValue({ - ok: true, - status: 200, - text: vi.fn().mockResolvedValue(payload), - }); + it('does not restore session snapshot during initiate (no exportSession call)', async () => { + const exportSessionMock = vi + .fn() + .mockResolvedValue(JSON.stringify({ info: {}, messages: [] })); const envWithIngest: PersistenceEnv = { ...mockEnv, SESSION_INGEST: { - fetch: fetchMock, + exportSession: exportSessionMock, } as unknown as PersistenceEnv['SESSION_INGEST'], }; @@ -194,6 +191,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_restore_test_skip'; mockedSetupWorkspace.mockResolvedValue({ @@ -214,7 +213,7 @@ describe('SessionService', () => { env: envWithIngest, }); - expect(fetchMock).not.toHaveBeenCalled(); + expect(exportSessionMock).not.toHaveBeenCalled(); expect(fakeSession.writeFile).not.toHaveBeenCalled(); expect(fakeSession.exec).not.toHaveBeenCalledWith( `kilo import "/tmp/kilo-session-export-${sessionId}.json"` @@ -235,6 +234,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_test_456'; mockedSetupWorkspace.mockResolvedValue({ @@ -281,6 +282,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const service = new SessionService(); const sessionId: SessionId = 'agent_test_456'; @@ -331,6 +334,8 @@ describe('SessionService', () => { const sandbox = { createSession: vi.fn().mockResolvedValue(fakeSession), mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_first_call'; mockedSetupWorkspace.mockResolvedValue({ @@ -395,6 +400,8 @@ describe('SessionService', () => { const sandbox = { createSession: vi.fn().mockResolvedValue(fakeSession), mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_resume_first_flag'; @@ -447,6 +454,8 @@ describe('SessionService', () => { const sandbox = { createSession: vi.fn().mockResolvedValue(fakeSession), mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const service = new SessionService(); @@ -499,6 +508,8 @@ describe('SessionService', () => { const sandbox = { createSession: vi.fn().mockResolvedValue(fakeSession), mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_capture_test'; mockedSetupWorkspace.mockResolvedValue({ @@ -574,6 +585,8 @@ describe('SessionService', () => { const sandbox = { createSession: vi.fn().mockResolvedValue(fakeSession), mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const mockDOGetMetadata = vi.fn(); @@ -598,6 +611,7 @@ describe('SessionService', () => { timestamp: 123456789, githubRepo: 'facebook/react', githubToken: 'test-token', + kiloSessionId: 'ses_test_kilo_session_id_0001', }; mockDOGetMetadata.mockResolvedValue(metadata); @@ -642,6 +656,8 @@ describe('SessionService', () => { const sandbox = { createSession: vi.fn().mockResolvedValue(fakeSession), mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const mockDOGetMetadata = vi.fn(); @@ -658,6 +674,7 @@ describe('SessionService', () => { }; // Mock: DO returns metadata with STALE token + const kiloSessionId = 'ses_test_kilo_session_id_0001'; const metadata = { version: 123456789, sessionId, @@ -665,7 +682,8 @@ describe('SessionService', () => { userId, timestamp: 123456789, githubRepo: 'facebook/react', - githubToken: 'stale-token-from-metadata', + githubToken: 'test-token', + kiloSessionId, }; mockDOGetMetadata.mockResolvedValue(metadata); @@ -709,6 +727,8 @@ describe('SessionService', () => { const sandbox = { createSession: vi.fn().mockResolvedValue(fakeSession), mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const mockDOGetMetadata = vi.fn(); @@ -733,6 +753,7 @@ describe('SessionService', () => { timestamp: 123456789, githubRepo: 'facebook/react', githubToken: 'metadata-token', + kiloSessionId: 'ses_test_kilo_session_id_0001', }; mockDOGetMetadata.mockResolvedValue(metadata); @@ -769,6 +790,8 @@ describe('SessionService', () => { const sandbox = { createSession: vi.fn().mockResolvedValue(fakeSession), mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const testEnv = { @@ -804,15 +827,11 @@ describe('SessionService', () => { it('restores session snapshot before reclone when workspace is missing', async () => { const mockDOGetMetadata = vi.fn(); const payload = JSON.stringify({ info: {}, messages: [] }); - const fetchMock = vi.fn().mockResolvedValue({ - ok: true, - status: 200, - text: vi.fn().mockResolvedValue(payload), - }); + const exportSessionMock = vi.fn().mockResolvedValue(payload); const envWithIngest: PersistenceEnv = { ...mockEnv, SESSION_INGEST: { - fetch: fetchMock, + exportSession: exportSessionMock, } as unknown as PersistenceEnv['SESSION_INGEST'], CLOUD_AGENT_SESSION: { idFromName: vi.fn(() => 'mock-do-id' as unknown as DurableObjectId), @@ -835,8 +854,11 @@ describe('SessionService', () => { const sandbox = { createSession: vi.fn().mockResolvedValue(fakeSession), mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; + const kiloSessionId = 'ses_test_kilo_session_id_0001'; const metadata = { version: 123456789, sessionId, @@ -845,6 +867,7 @@ describe('SessionService', () => { timestamp: 123456789, githubRepo: 'facebook/react', githubToken: 'test-token', + kiloSessionId, }; mockDOGetMetadata.mockResolvedValue(metadata); @@ -860,14 +883,10 @@ describe('SessionService', () => { env: envWithIngest, }); - expect(fetchMock).toHaveBeenCalledWith( - `https://session-ingest/api/session/${sessionId}/export`, - { - headers: { - Authorization: 'Bearer test-token', - }, - } - ); + expect(exportSessionMock).toHaveBeenCalledWith({ + sessionId: kiloSessionId, + kiloUserId: userId, + }); expect(fakeSession.writeFile).toHaveBeenCalledWith( `/tmp/kilo-session-export-${sessionId}.json`, payload @@ -895,6 +914,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_envtest_123'; mockedSetupWorkspace.mockResolvedValue({ @@ -952,6 +973,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_special_chars'; mockedSetupWorkspace.mockResolvedValue({ @@ -1001,6 +1024,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_no_env'; mockedSetupWorkspace.mockResolvedValue({ @@ -1051,6 +1076,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_gh_token_test'; mockedSetupWorkspace.mockResolvedValue({ @@ -1094,6 +1121,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_gh_token_override'; mockedSetupWorkspace.mockResolvedValue({ @@ -1142,6 +1171,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_no_gh_token'; mockedSetupWorkspace.mockResolvedValue({ @@ -1179,6 +1210,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_empty_gh_token'; mockedSetupWorkspace.mockResolvedValue({ @@ -1216,6 +1249,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_giturl_with_ghtoken'; mockedSetupWorkspace.mockResolvedValue({ @@ -1254,6 +1289,7 @@ describe('SessionService', () => { timestamp: 123456789, githubRepo: 'acme/repo', setupCommands: ['npm install', 'npm run build', 'npm test'], + kiloSessionId: 'ses_test_kilo_session_id_0001', }; const { env: testEnv } = createMetadataEnv({ @@ -1283,6 +1319,8 @@ describe('SessionService', () => { const sandbox = { createSession: vi.fn().mockResolvedValue(fakeSession), mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const service = new SessionService(); @@ -1337,6 +1375,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_failfast_test'; mockedSetupWorkspace.mockResolvedValue({ @@ -1382,6 +1422,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_timeout_test'; mockedSetupWorkspace.mockResolvedValue({ @@ -1420,6 +1462,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_cwd_test'; const workspacePath = `/workspace/org/user/sessions/${sessionId}`; @@ -1463,6 +1507,8 @@ describe('SessionService', () => { const sandbox = { createSession: sandboxCreateSession, mkdir: vi.fn().mockResolvedValue(undefined), + exec: vi.fn().mockResolvedValue({ exitCode: 0 }), + writeFile: vi.fn().mockResolvedValue(undefined), } as unknown as SandboxInstance; const sessionId: SessionId = 'agent_empty_commands'; mockedSetupWorkspace.mockResolvedValue({ @@ -1886,6 +1932,7 @@ describe('SessionService', () => { timestamp: 123456789, githubRepo: 'acme/repo', setupCommands: ['npm install', 'npm run build'], + kiloSessionId: 'ses_test_kilo_session_id_0001', }; const { env: testEnv } = createMetadataEnv({ @@ -1930,6 +1977,7 @@ describe('SessionService', () => { userId: 'user', timestamp: 123456789, githubRepo: 'acme/repo', + kiloSessionId: 'ses_test_kilo_session_id_0001', mcpServers: { puppeteer: { command: 'npx', @@ -2040,6 +2088,7 @@ describe('SessionService', () => { envVars: { API_KEY: 'test' }, setupCommands: ['npm install'], mcpServers: { test: { command: 'test-server' } }, + kiloSessionId: 'ses_test_kilo_session_id_0001', }; const { env: testEnv } = createMetadataEnv({ @@ -2708,127 +2757,6 @@ describe('SessionService', () => { }); }); - describe('linkKiloSessionInBackend', () => { - let originalFetch: typeof global.fetch; - - beforeEach(() => { - originalFetch = global.fetch; - }); - - afterEach(() => { - global.fetch = originalFetch; - }); - - it('should use correct tRPC wire format with request body', async () => { - const mockFetch = vi.fn().mockResolvedValue({ - ok: true, - json: () => Promise.resolve({ result: { data: { success: true } } }), - }); - global.fetch = mockFetch; - - const envWithBackendUrl: PersistenceEnv = { - ...mockEnv, - KILOCODE_BACKEND_BASE_URL: 'https://test.kilo.ai', - }; - - const service = new SessionService(); - // Access private method - await service['linkKiloSessionInBackend']( - 'kilo-session-123', - 'agent-session-456', - 'auth-token', - envWithBackendUrl - ); - - // Verify the request uses POST with body (not query string) - expect(mockFetch).toHaveBeenCalledWith( - 'https://test.kilo.ai/api/trpc/cliSessions.linkCloudAgent', - expect.objectContaining({ - method: 'POST', - headers: expect.objectContaining({ - Authorization: 'Bearer auth-token', - 'Content-Type': 'application/json', - }), - body: JSON.stringify({ - kilo_session_id: 'kilo-session-123', - cloud_agent_session_id: 'agent-session-456', - }), - }) - ); - }); - - it('should use default backend URL when not provided', async () => { - const mockFetch = vi.fn().mockResolvedValue({ - ok: true, - json: () => Promise.resolve({ result: { data: { success: true } } }), - }); - global.fetch = mockFetch; - - const service = new SessionService(); - await service['linkKiloSessionInBackend']( - 'kilo-session-123', - 'agent-session-456', - 'auth-token', - mockEnv // No KILOCODE_BACKEND_URL - ); - - const calledUrl = mockFetch.mock.calls[0]?.[0] as string; - expect(calledUrl).toContain('https://api.kilo.ai'); - }); - - it('should throw error when backend returns non-200', async () => { - global.fetch = vi.fn().mockResolvedValue({ - ok: false, - status: 404, - text: () => Promise.resolve('Not found'), - }); - - const service = new SessionService(); - await expect( - service['linkKiloSessionInBackend']( - 'kilo-session-123', - 'agent-session-456', - 'auth-token', - mockEnv - ) - ).rejects.toThrow('Failed to link sessions: 404'); - }); - - it('should throw error when backend does not confirm success', async () => { - global.fetch = vi.fn().mockResolvedValue({ - ok: true, - json: () => Promise.resolve({ result: { data: { success: false } } }), - }); - - const service = new SessionService(); - await expect( - service['linkKiloSessionInBackend']( - 'kilo-session-123', - 'agent-session-456', - 'auth-token', - mockEnv - ) - ).rejects.toThrow('Backend did not confirm successful link'); - }); - - it('should throw error when response format is unexpected', async () => { - global.fetch = vi.fn().mockResolvedValue({ - ok: true, - json: () => Promise.resolve({ unexpected: 'format' }), - }); - - const service = new SessionService(); - await expect( - service['linkKiloSessionInBackend']( - 'kilo-session-123', - 'agent-session-456', - 'auth-token', - mockEnv - ) - ).rejects.toThrow('Backend did not confirm successful link'); - }); - }); - describe('captureAndStoreBranch', () => { it('should capture current branch and update metadata', async () => { const updateUpstreamBranch = vi.fn().mockResolvedValue(undefined); diff --git a/cloud-agent-next/src/session-service.ts b/cloud-agent-next/src/session-service.ts index ad5bfc315..40be82a6e 100644 --- a/cloud-agent-next/src/session-service.ts +++ b/cloud-agent-next/src/session-service.ts @@ -8,7 +8,6 @@ import type { InterruptResult, } from './types.js'; import type { ExecutionParams as _ExecutionParams } from './schema.js'; -import { DEFAULT_BACKEND_URL } from './constants.js'; import { generateSandboxId } from './sandbox-id.js'; import { checkDiskSpace, @@ -289,6 +288,27 @@ export async function writeMCPSettings( .info('Configured MCP servers'); } +// Write Kilo auth file so the CLI's KiloSessions can call session ingest. +// The CLI reads ~/.local/share/kilo/auth.json via Auth.get("kilo") but we +// never run `kilo auth login` — credentials are injected purely via env vars +// for config (KILO_CONFIG_CONTENT). The session ingest code path ignores the +// provider config and only reads the auth file. +export async function writeAuthFile( + sandbox: SandboxInstance, + sessionHome: string, + kilocodeToken: string +): Promise { + const authDir = `${sessionHome}/.local/share/kilo`; + const authPath = `${authDir}/auth.json`; + + await sandbox.exec(`mkdir -p ${authDir}`); + + const authContent = JSON.stringify({ kilo: { type: 'api', key: kilocodeToken } }, null, 2); + await sandbox.writeFile(authPath, authContent); + + logger.info('Wrote kilo auth file for session ingest'); +} + /** * Fetch session metadata from Durable Object using RPC with retry logic. * Creates a fresh stub for each retry attempt as recommended by Cloudflare. @@ -553,6 +573,8 @@ export class SessionService { if (env.KILOCODE_BACKEND_BASE_URL) { envVars.KILOCODE_BACKEND_BASE_URL = env.KILOCODE_BACKEND_BASE_URL; + // Used by kilo server to check user auth to send to ingest + envVars.KILO_API_URL = env.KILOCODE_BACKEND_BASE_URL; } if (env.KILO_SESSION_INGEST_URL) { @@ -747,6 +769,9 @@ export class SessionService { await writeMCPSettings(sandbox, context.sessionHome, mcpServers); } + // Write auth file for session ingest + await writeAuthFile(sandbox, context.sessionHome, kilocodeToken); + // Save metadata to Durable Object const existingMetadata = await this.loadSessionMetadata(env, context); await this.saveSessionMetadata( @@ -769,7 +794,6 @@ export class SessionService { let isFirstCall = true; let capturedKiloSessionId: string | undefined = undefined; - const linkKiloSessionInBackend = this.linkKiloSessionInBackend.bind(this); const captureAndStoreBranch = this.captureAndStoreBranch.bind(this); return { @@ -804,16 +828,6 @@ export class SessionService { ) { capturedKiloSessionId = String(event.payload.sessionId); logger.setTags({ kiloSessionId: capturedKiloSessionId }); - void linkKiloSessionInBackend( - capturedKiloSessionId, - sessionId, - kilocodeToken, - env - ).catch((error: unknown) => { - logger - .withFields({ error: error instanceof Error ? error.message : String(error) }) - .error('Failed to link sessions in backend'); - }); } yield event; } @@ -826,38 +840,24 @@ export class SessionService { private async restoreSessionSnapshot( session: ExecutionSession, sessionId: string, - authToken: string, + kiloSessionId: string, env: PersistenceEnv, userId: string ): Promise { const tmpPath = `/tmp/kilo-session-export-${sessionId}.json`; let wroteSnapshot = false; try { - const response = await env.SESSION_INGEST.fetch( - `https://session-ingest/api/session/${sessionId}/export`, - { - headers: { - Authorization: `Bearer ${authToken}`, - }, - } - ); - - if (!response) { - throw new Error('Session ingest fetch returned no response'); - } + const payload = await env.SESSION_INGEST.exportSession({ + sessionId: kiloSessionId, + kiloUserId: userId, + }); - if (response.status === 401 || response.status === 404) { + if (payload === null) { throw new SessionSnapshotRestoreError( - `Session snapshot restore failed with status ${response.status}`, - response.status + `Session snapshot restore failed: session not found`, + 404 ); } - - if (!response.ok) { - throw new Error(`Session ingest returned ${response.status}`); - } - - const payload = await response.text(); await session.writeFile(tmpPath, payload); wroteSnapshot = true; @@ -949,7 +949,6 @@ export class SessionService { setupCommands, mcpServers, botId, - skipLinking, githubAppType, existingMetadata, } = options; @@ -1063,6 +1062,9 @@ export class SessionService { await writeMCPSettings(sandbox, context.sessionHome, mcpServers); } + // Write auth file for session ingest + await writeAuthFile(sandbox, context.sessionHome, kilocodeToken); + // Fetch metadata from DO if not provided, to ensure we preserve existing fields const metadataToPreserve = existingMetadata ?? (await this.loadSessionMetadata(env, context)) ?? undefined; @@ -1084,20 +1086,6 @@ export class SessionService { metadataToPreserve ); - // Skip linking if requested (e.g., for prepared sessions where backend already linked) - if (!skipLinking) { - try { - await this.linkKiloSessionInBackend(kiloSessionId, sessionId, kilocodeToken, env); - logger.info('Linked cloud-agent session to kilo session in backend'); - } catch (error) { - logger - .withFields({ error: error instanceof Error ? error.message : String(error) }) - .warn('Failed to link sessions in backend'); - } - } else { - logger.debug('Skipping backend linking (prepared session mode)'); - } - const captureAndStoreBranch = this.captureAndStoreBranch.bind(this); return { @@ -1300,7 +1288,12 @@ export class SessionService { } // Cold-start resume must restore snapshot or fail. - await this.restoreSessionSnapshot(session, sessionId, kilocodeToken, env, userId); + if (!metadata.kiloSessionId) { + throw new Error( + `Session ${sessionId} has no kiloSessionId in metadata. Cannot restore snapshot.` + ); + } + await this.restoreSessionSnapshot(session, sessionId, metadata.kiloSessionId, env, userId); await restoreWorkspace(session, context.workspacePath, context.branchName, { githubRepo: metadata.githubRepo, @@ -1321,6 +1314,9 @@ export class SessionService { if (metadata.mcpServers && Object.keys(metadata.mcpServers).length > 0) { await writeMCPSettings(sandbox, context.sessionHome, metadata.mcpServers); } + + // Re-write auth file (fresh clone) + await writeAuthFile(sandbox, context.sessionHome, kilocodeToken); } /** @@ -1625,144 +1621,61 @@ export class SessionService { } /** - * Create a minimal cliSession in kilocode-backend. - * Uses the customer's auth token (forwarded from the original request). - * Returns the generated kiloSessionId. + * Create a cli_sessions_v2 record via session-ingest RPC. + * Called during session preparation so the DB record exists before execution. */ - async createKiloSessionInBackend( + async createCliSessionViaSessionIngest( + kiloSessionId: string, cloudAgentSessionId: string, - authToken: string, + kiloUserId: string, env: PersistenceEnv, - organizationId?: string, - lastMode?: string, - lastModel?: string, - gitUrl?: string - ): Promise { - const backendUrl = env.KILOCODE_BACKEND_BASE_URL || DEFAULT_BACKEND_URL; - - const input = { - created_on_platform: 'cloud-agent', - organization_id: organizationId ?? null, - cloud_agent_session_id: cloudAgentSessionId, - version: 2, - last_mode: lastMode, - last_model: lastModel, - git_url: gitUrl, - }; - - const response = await fetch(`${backendUrl}/api/trpc/cliSessions.createV2`, { - method: 'POST', - headers: { - Authorization: `Bearer ${authToken}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify(input), - }); - - if (!response.ok) { - const text = await response.text(); - console.error('[createKiloSessionInBackend] Backend error:', { - status: response.status, - statusText: response.statusText, - body: '[redacted]', - backendUrl, - organizationId, + organizationId: string | undefined, + createdOnPlatform: string + ): Promise { + try { + await env.SESSION_INGEST.createSessionForCloudAgent({ + sessionId: kiloSessionId, + kiloUserId, cloudAgentSessionId, + organizationId, + createdOnPlatform, }); - throw new Error( - `Failed to create kilo session: ${response.status} - ${text.substring(0, 200)}` - ); - } - - const result = await response.json(); - - type TrpcResponse = { result?: { data?: { session_id?: string } } }; - const typedResult = result as TrpcResponse; - - const sessionId = typedResult.result?.data?.session_id; - if (!sessionId) { - throw new Error('Backend did not return session_id'); - } - - return sessionId; - } - - /** - * Delete a cliSession in kilocode-backend. - * Used for rollback when DO prepare() fails after backend session was created. - */ - async deleteKiloSessionInBackend( - kiloSessionId: string, - authToken: string, - env: PersistenceEnv - ): Promise { - const backendUrl = env.KILOCODE_BACKEND_BASE_URL || DEFAULT_BACKEND_URL; - - const input = { - session_id: kiloSessionId, - }; - - const response = await fetch(`${backendUrl}/api/trpc/cliSessions.delete`, { - method: 'POST', - headers: { - Authorization: `Bearer ${authToken}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify(input), - }); - - if (!response.ok) { - const text = await response.text(); - throw new Error(`Failed to delete kilo session: ${response.status} ${text}`); - } - - const result = await response.json(); - - type TrpcResponse = { result?: { data?: { success?: boolean } } }; - const typedResult = result as TrpcResponse; - - if (!typedResult.result?.data?.success) { - throw new Error('Backend did not confirm successful deletion'); + } catch (error) { + logger + .withFields({ + kiloSessionId, + cloudAgentSessionId, + kiloUserId, + error: error instanceof Error ? error.message : String(error), + }) + .error('session-ingest RPC createSessionForCloudAgent failed'); + throw error; } } /** - * Helper to link sessions in backend using tRPC wire format + * Delete a cli_sessions_v2 record via session-ingest RPC. + * Used for rollback when DO prepare() fails after the record was created. */ - private async linkKiloSessionInBackend( + async deleteCliSessionViaSessionIngest( kiloSessionId: string, - cloudAgentSessionId: string, - authToken: string, + kiloUserId: string, env: PersistenceEnv ): Promise { - const backendUrl = env.KILOCODE_BACKEND_BASE_URL || DEFAULT_BACKEND_URL; - - const input = { - kilo_session_id: kiloSessionId, - cloud_agent_session_id: cloudAgentSessionId, - }; - - const response = await fetch(`${backendUrl}/api/trpc/cliSessions.linkCloudAgent`, { - method: 'POST', - headers: { - Authorization: `Bearer ${authToken}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify(input), - }); - - if (!response.ok) { - const text = await response.text(); - throw new Error(`Failed to link sessions: ${response.status} ${text}`); - } - - const result = await response.json(); - - type TrpcResponse = { result?: { data?: { success?: boolean } } }; - const typedResult = result as TrpcResponse; - - if (!typedResult.result?.data?.success) { - throw new Error('Backend did not confirm successful link'); + try { + await env.SESSION_INGEST.deleteSessionForCloudAgent({ + sessionId: kiloSessionId, + kiloUserId, + }); + } catch (error) { + logger + .withFields({ + kiloSessionId, + kiloUserId, + error: error instanceof Error ? error.message : String(error), + }) + .error('session-ingest RPC deleteSessionForCloudAgent failed'); + throw error; } } @@ -1908,7 +1821,6 @@ type InitiateFromKiloSessionBaseOptions = { setupCommands?: string[]; mcpServers?: Record; botId?: string; - skipLinking?: boolean; /** GitHub App type for selecting correct slug/bot identity */ githubAppType?: 'standard' | 'lite'; /** diff --git a/cloud-agent-next/src/session/ingest-handlers/kilo-session-capture.ts b/cloud-agent-next/src/session/ingest-handlers/kilo-session-capture.ts index 472235165..2c3d2599f 100644 --- a/cloud-agent-next/src/session/ingest-handlers/kilo-session-capture.ts +++ b/cloud-agent-next/src/session/ingest-handlers/kilo-session-capture.ts @@ -4,7 +4,6 @@ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12 export type KiloSessionCaptureContext = { updateKiloSessionId: (id: string) => Promise; - linkToBackend: (kiloSessionId: string) => Promise; logger: { info: (msg: string, data?: object) => void; warn: (msg: string, data?: object) => void; @@ -38,13 +37,5 @@ export async function handleKilocodeEvent( await ctx.updateKiloSessionId(kiloSessionId); ctx.logger.info('Captured kiloSessionId', { kiloSessionId }); - // Backend link is async/non-blocking - void ctx.linkToBackend(kiloSessionId).catch(err => { - ctx.logger.warn('Failed to link kiloSessionId to backend', { - kiloSessionId, - error: err instanceof Error ? err.message : String(err), - }); - }); - return true; } diff --git a/cloud-agent-next/src/types.ts b/cloud-agent-next/src/types.ts index 60261fa25..3935e543e 100644 --- a/cloud-agent-next/src/types.ts +++ b/cloud-agent-next/src/types.ts @@ -1,6 +1,7 @@ import type { getSandbox, ExecutionSession, Sandbox } from '@cloudflare/sandbox'; import type { CloudAgentSession } from './persistence/CloudAgentSession.js'; import type { CallbackJob } from './callbacks/index.js'; +import type { SessionIngestBinding } from './session-ingest-binding.js'; import * as z from 'zod'; import { Limits } from './schema.js'; @@ -93,7 +94,7 @@ export type Env = { /** Durable Object namespace for CloudAgentSession metadata (SQLite-backed) with RPC support */ CLOUD_AGENT_SESSION: DurableObjectNamespace; /** Service binding for the session ingest worker */ - SESSION_INGEST: Fetcher; + SESSION_INGEST: SessionIngestBinding; /** Queue for callback messages (optional - supports incremental rollout) */ CALLBACK_QUEUE?: Queue; /** KV namespace for caching GitHub installation tokens */ diff --git a/cloud-agent-next/src/websocket/ingest.ts b/cloud-agent-next/src/websocket/ingest.ts index 5189e5965..5882a36ea 100644 --- a/cloud-agent-next/src/websocket/ingest.ts +++ b/cloud-agent-next/src/websocket/ingest.ts @@ -110,8 +110,6 @@ export type ExecutionData = { export type IngestDOContext = { /** Persist the kiloSessionId in DO metadata */ updateKiloSessionId: (id: string) => Promise; - /** Link kiloSessionId to backend for analytics */ - linkKiloSessionInBackend: (id: string) => Promise; /** Persist the upstream branch in DO metadata */ updateUpstreamBranch: (branch: string) => Promise; /** Clear the active execution when done */ @@ -342,7 +340,6 @@ export function createIngestHandler( attachment.kiloSessionState, { updateKiloSessionId: id => doContext.updateKiloSessionId(id), - linkToBackend: id => doContext.linkKiloSessionInBackend(id), logger: console, } ); diff --git a/cloud-agent-next/wrangler.jsonc b/cloud-agent-next/wrangler.jsonc index 40e7bdccd..04327fced 100644 --- a/cloud-agent-next/wrangler.jsonc +++ b/cloud-agent-next/wrangler.jsonc @@ -73,6 +73,7 @@ { "binding": "SESSION_INGEST", "service": "session-ingest", + "entrypoint": "SessionIngestRPC", }, ], /** @@ -117,7 +118,7 @@ "image": "./Dockerfile", "instance_type": "standard-4", "image_vars": { - "KILOCODE_CLI_VERSION": "1.0.16", + "KILOCODE_CLI_VERSION": "1.0.22", }, "max_instances": 20, "rollout_active_grace_period": 120, @@ -228,6 +229,7 @@ { "binding": "SESSION_INGEST", "service": "session-ingest", + "entrypoint": "SessionIngestRPC", }, ], "r2_buckets": [ diff --git a/cloudflare-app-builder/start-dev.sh b/cloudflare-app-builder/start-dev.sh index ed2ad78d9..273e8c66d 100755 --- a/cloudflare-app-builder/start-dev.sh +++ b/cloudflare-app-builder/start-dev.sh @@ -5,7 +5,10 @@ # # Services started: # - cloudflare-db-proxy (port 8792) +# - cloudflare-session-ingest (port 8787) # - cloud-agent (port 8788) +# - cloud-agent-next (port 8794) +# - cloudflare-git-token-service (port 8795) # - cloudflare-app-builder (port 8790) # - ngrok (forwarding to port 8790) # @@ -75,13 +78,16 @@ fi # Create new tmux session with first window for db-proxy tmux new-session -d -s "$SESSION_NAME" -n "services" -c "$PROJECT_ROOT" -# Split into 2x2 grid +# Split into grid for 7 services # First split horizontally (top/bottom) tmux split-window -v -t "$SESSION_NAME:services" -c "$PROJECT_ROOT" -# Split top pane vertically (left/right) +# Split top pane vertically into 3 tmux split-window -h -t "$SESSION_NAME:services.0" -c "$PROJECT_ROOT" -# Split bottom pane vertically (left/right) -tmux split-window -h -t "$SESSION_NAME:services.2" -c "$PROJECT_ROOT" +tmux split-window -h -t "$SESSION_NAME:services.0" -c "$PROJECT_ROOT" +# Split bottom pane vertically into 4 +tmux split-window -h -t "$SESSION_NAME:services.3" -c "$PROJECT_ROOT" +tmux split-window -h -t "$SESSION_NAME:services.3" -c "$PROJECT_ROOT" +tmux split-window -h -t "$SESSION_NAME:services.3" -c "$PROJECT_ROOT" # Arrange panes in a tiled layout tmux select-layout -t "$SESSION_NAME:services" tiled @@ -96,30 +102,45 @@ tmux select-pane -t "$SESSION_NAME:services.0" -T "db-proxy (8792)" # Using different inspector ports to avoid conflicts (default is 9229) tmux send-keys -t "$SESSION_NAME:services.0" "cd $PROJECT_ROOT/cloudflare-db-proxy && echo '🗄️ Starting cloudflare-db-proxy (port 8792)...' && pnpm exec wrangler dev --inspector-port 9230" C-m -# Pane 1 (top-right): cloud-agent -tmux select-pane -t "$SESSION_NAME:services.1" -T "cloud-agent (8788)" -tmux send-keys -t "$SESSION_NAME:services.1" "cd $PROJECT_ROOT/cloud-agent && echo '🤖 Starting cloud-agent (port 8788)...' && pnpm exec wrangler dev --inspector-port 9231" C-m +# Pane 1 (top-middle): cloudflare-session-ingest +tmux select-pane -t "$SESSION_NAME:services.1" -T "session-ingest (8787)" +tmux send-keys -t "$SESSION_NAME:services.1" "cd $PROJECT_ROOT/cloudflare-session-ingest && echo '📥 Starting cloudflare-session-ingest (port 8787)...' && pnpm exec wrangler dev --inspector-port 9233" C-m + +# Pane 2 (top-right): cloud-agent +tmux select-pane -t "$SESSION_NAME:services.2" -T "cloud-agent (8788)" +tmux send-keys -t "$SESSION_NAME:services.2" "cd $PROJECT_ROOT/cloud-agent && echo '🤖 Starting cloud-agent (port 8788)...' && pnpm exec wrangler dev --inspector-port 9231" C-m + +# Pane 3 (bottom-left): cloudflare-git-token-service +tmux select-pane -t "$SESSION_NAME:services.3" -T "git-token-service (8795)" +tmux send-keys -t "$SESSION_NAME:services.3" "cd $PROJECT_ROOT/cloudflare-git-token-service && echo '🔑 Starting cloudflare-git-token-service (port 8795)...' && pnpm exec wrangler dev --inspector-port 9235" C-m + +# Pane 4: cloudflare-app-builder +tmux select-pane -t "$SESSION_NAME:services.4" -T "app-builder (8790)" +tmux send-keys -t "$SESSION_NAME:services.4" "cd $PROJECT_ROOT/cloudflare-app-builder && echo '🏗️ Starting cloudflare-app-builder (port 8790)...' && pnpm exec wrangler dev --inspector-port 9232" C-m -# Pane 2 (bottom-left): cloudflare-app-builder -tmux select-pane -t "$SESSION_NAME:services.2" -T "app-builder (8790)" -tmux send-keys -t "$SESSION_NAME:services.2" "cd $PROJECT_ROOT/cloudflare-app-builder && echo '🏗️ Starting cloudflare-app-builder (port 8790)...' && pnpm exec wrangler dev --inspector-port 9232" C-m +# Pane 5: ngrok +tmux select-pane -t "$SESSION_NAME:services.5" -T "ngrok → 8790" +tmux send-keys -t "$SESSION_NAME:services.5" "echo '🌐 Starting ngrok (forwarding to port 8790)...' && ngrok http 8790" C-m -# Pane 3 (bottom-right): ngrok -tmux select-pane -t "$SESSION_NAME:services.3" -T "ngrok → 8790" -tmux send-keys -t "$SESSION_NAME:services.3" "echo '🌐 Starting ngrok (forwarding to port 8790)...' && ngrok http 8790" C-m +# Pane 6 (bottom-right): cloud-agent-next +tmux select-pane -t "$SESSION_NAME:services.6" -T "cloud-agent-next (8794)" +tmux send-keys -t "$SESSION_NAME:services.6" "cd $PROJECT_ROOT/cloud-agent-next && echo '☁️ Starting cloud-agent-next (port 8794)...' && pnpm exec wrangler dev --env dev --inspector-port 9234" C-m # Select the app-builder pane by default -tmux select-pane -t "$SESSION_NAME:services.2" +tmux select-pane -t "$SESSION_NAME:services.5" echo "" echo "╔══════════════════════════════════════════════════════════════════╗" echo "║ App Builder Dev Environment Started! 🚀 ║" echo "╠══════════════════════════════════════════════════════════════════╣" echo "║ Services: ║" -echo "║ • cloudflare-db-proxy → http://localhost:8792 ║" -echo "║ • cloud-agent → http://localhost:8788 ║" -echo "║ • cloudflare-app-builder → http://localhost:8790 ║" -echo "║ • ngrok → forwarding to :8790 ║" +echo "║ • cloudflare-db-proxy → http://localhost:8792 ║" +echo "║ • cloudflare-session-ingest → http://localhost:8787 ║" +echo "║ • cloud-agent → http://localhost:8788 ║" +echo "║ • cloud-agent-next → http://localhost:8794 ║" +echo "║ • git-token-service → http://localhost:8795 ║" +echo "║ • cloudflare-app-builder → http://localhost:8790 ║" +echo "║ • ngrok → forwarding to :8790 ║" echo "╠══════════════════════════════════════════════════════════════════╣" echo "║ tmux Navigation: ║" echo "║ Switch panes: Ctrl+b then arrow keys ║" diff --git a/cloudflare-git-token-service/wrangler.jsonc b/cloudflare-git-token-service/wrangler.jsonc index 91e8a2bf1..b13504d99 100644 --- a/cloudflare-git-token-service/wrangler.jsonc +++ b/cloudflare-git-token-service/wrangler.jsonc @@ -25,7 +25,7 @@ }, ], "dev": { - "port": 8794, + "port": 8795, }, "env": { "dev": { diff --git a/cloudflare-session-ingest/package.json b/cloudflare-session-ingest/package.json index 686a68d3e..84cc0c9a3 100644 --- a/cloudflare-session-ingest/package.json +++ b/cloudflare-session-ingest/package.json @@ -19,7 +19,7 @@ "@cloudflare/workers-types": "^4.20260120.0", "@types/jsonwebtoken": "^9.0.10", "@types/node": "^22", - "vitest": "^2.1.8", + "vitest": "^3.2.4", "wrangler": "^4.61.0" } } diff --git a/cloudflare-session-ingest/src/db/kysely.ts b/cloudflare-session-ingest/src/db/kysely.ts index 87258c768..1b1cfd1e0 100644 --- a/cloudflare-session-ingest/src/db/kysely.ts +++ b/cloudflare-session-ingest/src/db/kysely.ts @@ -14,6 +14,7 @@ types.setTypeParser(types.builtins.INT8, val => parseInt(val, 10)); export type CliSessionsV2Table = { session_id: string; kilo_user_id: string; + cloud_agent_session_id: Generated; version: ColumnType; public_id: Generated; parent_session_id: Generated; diff --git a/cloudflare-session-ingest/src/index.test.ts b/cloudflare-session-ingest/src/index.test.ts index 45c69b3ee..524a03c83 100644 --- a/cloudflare-session-ingest/src/index.test.ts +++ b/cloudflare-session-ingest/src/index.test.ts @@ -1,9 +1,20 @@ -import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; vi.mock('cloudflare:workers', () => ({ DurableObject: class DurableObject { constructor(_state: unknown, _env: unknown) {} }, + WorkerEntrypoint: class WorkerEntrypoint { + env: unknown; + ctx: ExecutionContext; + constructor() { + this.env = undefined; + this.ctx = { + waitUntil: () => {}, + passThroughOnException: () => {}, + } as unknown as ExecutionContext; + } + }, })); vi.mock('./db/kysely', () => ({ @@ -14,11 +25,10 @@ vi.mock('./dos/SessionIngestDO', () => ({ getSessionIngestDO: vi.fn(), })); +import app from './index'; import { getDb } from './db/kysely'; import { getSessionIngestDO } from './dos/SessionIngestDO'; -let app: { fetch: (req: Request, env: TestBindings) => Response | Promise }; - type TestBindings = { HYPERDRIVE: { connectionString: string }; SESSION_INGEST_DO: unknown; @@ -42,26 +52,21 @@ function makeDbFakes() { return { db, selectExecuteTakeFirst }; } -describe('public session route', () => { - beforeAll(async () => { - const mod = await import('./index'); - app = mod.default; - }); +const defaultEnv: TestBindings = { + HYPERDRIVE: { connectionString: 'postgres://test' }, + SESSION_INGEST_DO: {}, + SESSION_ACCESS_CACHE_DO: {}, + NEXTAUTH_SECRET: {}, + NEXTAUTH_SECRET_RAW: 'secret', +}; +describe('public session route', () => { beforeEach(() => { vi.resetAllMocks(); }); it('returns 400 for invalid uuid', async () => { - const env: TestBindings = { - HYPERDRIVE: { connectionString: 'postgres://test' }, - SESSION_INGEST_DO: {}, - SESSION_ACCESS_CACHE_DO: {}, - NEXTAUTH_SECRET: {}, - NEXTAUTH_SECRET_RAW: 'secret', - }; - - const res = await app.fetch(new Request('http://local/session/not-a-uuid'), env); + const res = await app.request('/session/not-a-uuid', {}, defaultEnv); expect(res.status).toBe(400); }); @@ -70,18 +75,7 @@ describe('public session route', () => { vi.mocked(getDb).mockReturnValue(db as never); selectExecuteTakeFirst.mockResolvedValueOnce(undefined); - const env: TestBindings = { - HYPERDRIVE: { connectionString: 'postgres://test' }, - SESSION_INGEST_DO: {}, - SESSION_ACCESS_CACHE_DO: {}, - NEXTAUTH_SECRET: {}, - NEXTAUTH_SECRET_RAW: 'secret', - }; - - const res = await app.fetch( - new Request('http://local/session/11111111-1111-4111-8111-111111111111'), - env - ); + const res = await app.request('/session/11111111-1111-4111-8111-111111111111', {}, defaultEnv); expect(res.status).toBe(404); }); @@ -101,18 +95,7 @@ describe('public session route', () => { stub as unknown as ReturnType ); - const env: TestBindings = { - HYPERDRIVE: { connectionString: 'postgres://test' }, - SESSION_INGEST_DO: {}, - SESSION_ACCESS_CACHE_DO: {}, - NEXTAUTH_SECRET: {}, - NEXTAUTH_SECRET_RAW: 'secret', - }; - - const res = await app.fetch( - new Request('http://local/session/11111111-1111-4111-8111-111111111111'), - env - ); + const res = await app.request('/session/11111111-1111-4111-8111-111111111111', {}, defaultEnv); expect(res.status).toBe(200); expect(res.headers.get('content-type')).toBe('application/json; charset=utf-8'); diff --git a/cloudflare-session-ingest/src/index.ts b/cloudflare-session-ingest/src/index.ts index 41126dcc1..c7593fae0 100644 --- a/cloudflare-session-ingest/src/index.ts +++ b/cloudflare-session-ingest/src/index.ts @@ -8,6 +8,7 @@ import { getSessionIngestDO } from './dos/SessionIngestDO'; import { withDORetry } from './util/do-retry'; export { SessionIngestDO } from './dos/SessionIngestDO'; export { SessionAccessCacheDO } from './dos/SessionAccessCacheDO'; +export { SessionIngestRPC } from './session-ingest-rpc'; const app = new Hono<{ Bindings: Env; diff --git a/cloudflare-session-ingest/src/routes/api.ts b/cloudflare-session-ingest/src/routes/api.ts index f2903320f..e5240569e 100644 --- a/cloudflare-session-ingest/src/routes/api.ts +++ b/cloudflare-session-ingest/src/routes/api.ts @@ -9,6 +9,7 @@ import { getSessionAccessCacheDO } from '../dos/SessionAccessCacheDO'; import { SessionSyncInputSchema } from '../types/session-sync'; import { withDORetry } from '../util/do-retry'; import { splitIngestBatchForDO } from '../util/ingest-batching'; +import { getSessionExport } from '../services/session-export'; export type ApiContext = { Bindings: Env; @@ -293,26 +294,13 @@ api.get('/session/:sessionId/export', async c => { return c.json({ success: false, error: 'Invalid sessionId', issues: parsed.error.issues }, 400); } - const db = getDb(c.env.HYPERDRIVE); const kiloUserId = c.get('user_id'); + const json = await getSessionExport(c.env, parsed.data, kiloUserId); - const session = await db - .selectFrom('cli_sessions_v2') - .select(['session_id']) - .where('session_id', '=', parsed.data) - .where('kilo_user_id', '=', kiloUserId) - .executeTakeFirst(); - - if (!session) { + if (json === null) { return c.json({ success: false, error: 'session_not_found' }, 404); } - const json = await withDORetry( - () => getSessionIngestDO(c.env, { kiloUserId, sessionId: parsed.data }), - stub => stub.getAll(), - 'SessionIngestDO.getAll' - ); - return c.body(json, 200, { 'content-type': 'application/json; charset=utf-8', }); diff --git a/cloudflare-session-ingest/src/services/session-export.ts b/cloudflare-session-ingest/src/services/session-export.ts new file mode 100644 index 000000000..a6f5a7432 --- /dev/null +++ b/cloudflare-session-ingest/src/services/session-export.ts @@ -0,0 +1,38 @@ +import type { Env } from '../env'; +import { getDb } from '../db/kysely'; +import { getSessionIngestDO } from '../dos/SessionIngestDO'; +import { withDORetry } from '../util/do-retry'; + +/** + * Fetch the full session export payload from the SessionIngestDO. + * + * Verifies that the session exists in `cli_sessions_v2` and belongs to the + * given user before reading the DO. + * + * @returns The raw JSON string from `SessionIngestDO.getAll()`, or `null` + * if the session does not exist or does not belong to the user. + */ +export async function getSessionExport( + env: Env, + sessionId: string, + kiloUserId: string +): Promise { + const db = getDb(env.HYPERDRIVE); + + const session = await db + .selectFrom('cli_sessions_v2') + .select(['session_id']) + .where('session_id', '=', sessionId) + .where('kilo_user_id', '=', kiloUserId) + .executeTakeFirst(); + + if (!session) { + return null; + } + + return withDORetry( + () => getSessionIngestDO(env, { kiloUserId, sessionId }), + stub => stub.getAll(), + 'SessionIngestDO.getAll' + ); +} diff --git a/cloudflare-session-ingest/src/session-ingest-rpc.ts b/cloudflare-session-ingest/src/session-ingest-rpc.ts new file mode 100644 index 000000000..030deb65b --- /dev/null +++ b/cloudflare-session-ingest/src/session-ingest-rpc.ts @@ -0,0 +1,157 @@ +import { WorkerEntrypoint } from 'cloudflare:workers'; +import { z } from 'zod'; +import type { Env } from './env'; +import { getDb } from './db/kysely'; +import { getSessionIngestDO } from './dos/SessionIngestDO'; +import { getSessionAccessCacheDO } from './dos/SessionAccessCacheDO'; +import { withDORetry } from './util/do-retry'; +import { getSessionExport } from './services/session-export'; + +const sessionIdSchema = z.string().startsWith('ses_').length(30); + +export class SessionIngestRPC extends WorkerEntrypoint { + /** + * RPC method: export session data from the SessionIngestDO. + * Called via service binding from cloud-agent-next during session restore. + * + * Returns the raw JSON string from the DO, or null if the session + * does not exist or does not belong to the given user. + */ + async exportSession(params: { sessionId: string; kiloUserId: string }): Promise { + const parsed = z + .object({ + sessionId: sessionIdSchema, + kiloUserId: z.string().min(1), + }) + .parse(params); + + return getSessionExport(this.env, parsed.sessionId, parsed.kiloUserId); + } + + /** + * RPC method: create a cli_sessions_v2 record for a cloud-agent-next session. + * Called via service binding from cloud-agent-next during session preparation. + * + * Uses ON CONFLICT DO UPDATE to set cloud_agent_session_id (and organization_id + * if provided), matching the behavior previously in the backend routers. + */ + async createSessionForCloudAgent(params: { + sessionId: string; + kiloUserId: string; + cloudAgentSessionId: string; + organizationId?: string; + createdOnPlatform: string; + }): Promise { + const parsed = z + .object({ + sessionId: sessionIdSchema, + kiloUserId: z.string().min(1), + cloudAgentSessionId: z.string().min(1), + organizationId: z.string().optional(), + createdOnPlatform: z.string().min(1), + }) + .parse(params); + + const db = getDb(this.env.HYPERDRIVE); + + await db + .insertInto('cli_sessions_v2') + .values({ + session_id: parsed.sessionId, + kilo_user_id: parsed.kiloUserId, + cloud_agent_session_id: parsed.cloudAgentSessionId, + organization_id: parsed.organizationId ?? null, + created_on_platform: parsed.createdOnPlatform, + version: 0, + }) + .onConflict(oc => + oc.columns(['session_id', 'kilo_user_id']).doUpdateSet({ + cloud_agent_session_id: parsed.cloudAgentSessionId, + ...(parsed.organizationId !== undefined + ? { organization_id: parsed.organizationId } + : {}), + }) + ) + .execute(); + + // Warm the session cache so subsequent ingests can skip Postgres. + // Best-effort: cache miss is acceptable; don't fail the create if the DO is unavailable. + try { + await withDORetry( + () => getSessionAccessCacheDO(this.env, { kiloUserId: parsed.kiloUserId }), + sessionCache => sessionCache.add(parsed.sessionId), + 'SessionAccessCacheDO.add' + ); + } catch (cacheError) { + console.error('Failed to warm session cache after create (non-fatal)', { + sessionId: parsed.sessionId, + kiloUserId: parsed.kiloUserId, + error: cacheError instanceof Error ? cacheError.message : String(cacheError), + }); + } + } + + /** + * RPC method: delete a cli_sessions_v2 record for a cloud-agent-next session. + * Called via service binding from cloud-agent-next for rollback when DO prepare() fails. + * + * Scoped to the user (composite PK: session_id + kilo_user_id). + */ + async deleteSessionForCloudAgent(params: { + sessionId: string; + kiloUserId: string; + }): Promise { + const parsed = z + .object({ + sessionId: sessionIdSchema, + kiloUserId: z.string().min(1), + }) + .parse(params); + + const db = getDb(this.env.HYPERDRIVE); + + await db + .deleteFrom('cli_sessions_v2') + .where('session_id', '=', parsed.sessionId) + .where('kilo_user_id', '=', parsed.kiloUserId) + .execute(); + + // Clear caches — best-effort; don't fail the delete if DOs are unavailable. + const cacheErrors: string[] = []; + try { + await withDORetry( + () => getSessionAccessCacheDO(this.env, { kiloUserId: parsed.kiloUserId }), + sessionCache => sessionCache.remove(parsed.sessionId), + 'SessionAccessCacheDO.remove' + ); + } catch (error) { + cacheErrors.push( + `SessionAccessCacheDO.remove: ${error instanceof Error ? error.message : String(error)}` + ); + } + + try { + await withDORetry( + () => + getSessionIngestDO(this.env, { + kiloUserId: parsed.kiloUserId, + sessionId: parsed.sessionId, + }), + stub => stub.clear(), + 'SessionIngestDO.clear' + ); + } catch (error) { + cacheErrors.push( + `SessionIngestDO.clear: ${error instanceof Error ? error.message : String(error)}` + ); + } + + if (cacheErrors.length > 0) { + console.error('Failed to clear caches after delete (non-fatal)', { + sessionId: parsed.sessionId, + kiloUserId: parsed.kiloUserId, + errors: cacheErrors, + }); + } + } +} diff --git a/cloudflare-session-ingest/wrangler.jsonc b/cloudflare-session-ingest/wrangler.jsonc index 2acb6560f..f7a77d93a 100644 --- a/cloudflare-session-ingest/wrangler.jsonc +++ b/cloudflare-session-ingest/wrangler.jsonc @@ -5,6 +5,11 @@ "main": "src/index.ts", "compatibility_date": "2026-01-27", "compatibility_flags": ["nodejs_compat"], + "dev": { + "port": 8787, + "local_protocol": "http", + "ip": "0.0.0.0", + }, "placement": { "mode": "smart", }, @@ -53,6 +58,8 @@ "binding": "NEXTAUTH_SECRET_PROD", "store_id": "342a86d9e3a94da698e82d0c6e2a36f0", "secret_name": "NEXTAUTH_SECRET_PROD", + // To set: wrangler secrets-store secret create 342a86d9e3a94da698e82d0c6e2a36f0 --name NEXTAUTH_SECRET_PROD --scopes workers + // docs https://developers.cloudflare.com/workers/wrangler/commands/#secrets-store-secret }, ], } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12edaee4c..3f3ff5711 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,7 +65,7 @@ importers: version: 15.5.11(@mdx-js/loader@3.1.1(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.25.12)))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.2.0)) '@next/third-parties': specifier: ^15.5.10 - version: 15.5.11(next@15.5.11(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0) + version: 15.5.11(next@15.5.11(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0) '@octokit/auth-app': specifier: ^8.1.2 version: 8.1.2 @@ -128,7 +128,7 @@ importers: version: 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@sentry/nextjs': specifier: ^10.29.0 - version: 10.29.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@15.5.11(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.25.12)) + version: 10.29.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@15.5.11(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.25.12)) '@sentry/opentelemetry': specifier: ^10.29.0 version: 10.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) @@ -212,10 +212,10 @@ importers: version: 3.0.6 jotai: specifier: ^2.15.1 - version: 2.15.1(@babel/core@7.28.4)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0) + version: 2.15.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0) jotai-minidb: specifier: ^0.0.8 - version: 0.0.8(jotai@2.15.1(@babel/core@7.28.4)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0)) + version: 0.0.8(jotai@2.15.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0)) js-cookie: specifier: ^3.0.5 version: 3.0.5 @@ -233,10 +233,10 @@ importers: version: 12.23.24(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) next: specifier: ^15.5.10 - version: 15.5.11(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 15.5.11(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) next-auth: specifier: ^4.24.13 - version: 4.24.13(next@15.5.11(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 4.24.13(next@15.5.11(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) openai: specifier: ^6.8.1 version: 6.8.1(ws@8.18.2)(zod@4.3.4) @@ -414,7 +414,7 @@ importers: version: 4.1.17 ts-jest: specifier: ^29.4.5 - version: 29.4.5(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(esbuild@0.25.12)(jest-util@30.2.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@swc/core@1.12.5)(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@swc/core@1.12.5)(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) tsconfig-paths: specifier: ^4.2.0 version: 4.2.0 @@ -930,8 +930,8 @@ importers: specifier: ^22 version: 22.19.1 vitest: - specifier: ^2.1.8 - version: 2.1.9(@types/node@22.19.1)(@vitest/ui@2.1.9)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + specifier: ^3.2.4 + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.1)(@vitest/ui@3.2.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) wrangler: specifier: ^4.61.0 version: 4.61.1(@cloudflare/workers-types@4.20260130.0) @@ -6007,26 +6007,12 @@ packages: '@vitest/browser': optional: true - '@vitest/expect@2.1.9': - resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} - '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} '@vitest/expect@4.0.18': resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} - '@vitest/mocker@2.1.9': - resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} - peerDependencies: - msw: ^2.4.9 - vite: '>=6.4.1' - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - '@vitest/mocker@3.2.4': resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} peerDependencies: @@ -6049,55 +6035,35 @@ packages: vite: optional: true - '@vitest/pretty-format@2.1.9': - resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} - '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} '@vitest/pretty-format@4.0.18': resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} - '@vitest/runner@2.1.9': - resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} - '@vitest/runner@3.2.4': resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} '@vitest/runner@4.0.18': resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} - '@vitest/snapshot@2.1.9': - resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} - '@vitest/snapshot@3.2.4': resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} '@vitest/snapshot@4.0.18': resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} - '@vitest/spy@2.1.9': - resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} - '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} '@vitest/spy@4.0.18': resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} - '@vitest/ui@2.1.9': - resolution: {integrity: sha512-izzd2zmnk8Nl5ECYkW27328RbQ1nKvkm6Bb5DAaz1Gk59EbLkiCMa6OLT0NoaAYTjOFS6N+SMYW1nh4/9ljPiw==} - peerDependencies: - vitest: 2.1.9 - '@vitest/ui@3.2.4': resolution: {integrity: sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==} peerDependencies: vitest: 3.2.4 - '@vitest/utils@2.1.9': - resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} - '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} @@ -10004,9 +9970,6 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -11296,10 +11259,6 @@ packages: resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} - tinyrainbow@1.2.0: - resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} - engines: {node: '>=14.0.0'} - tinyrainbow@2.0.0: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} @@ -11308,10 +11267,6 @@ packages: resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} - tinyspy@3.0.2: - resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} - engines: {node: '>=14.0.0'} - tinyspy@4.0.4: resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} engines: {node: '>=14.0.0'} @@ -11705,11 +11660,6 @@ packages: victory-vendor@37.3.6: resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} - vite-node@2.1.9: - resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - vite-node@3.2.4: resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -11755,31 +11705,6 @@ packages: yaml: optional: true - vitest@2.1.9: - resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.1.9 - '@vitest/ui': 2.1.9 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - vitest@3.2.4: resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -12992,45 +12917,21 @@ snapshots: dependencies: '@babel/core': 7.28.5 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -13046,34 +12947,16 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -13084,89 +12967,41 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -14972,9 +14807,9 @@ snapshots: '@next/swc-win32-x64-msvc@15.5.7': optional: true - '@next/third-parties@15.5.11(next@15.5.11(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)': + '@next/third-parties@15.5.11(next@15.5.11(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)': dependencies: - next: 15.5.11(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next: 15.5.11(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 third-party-capital: 1.0.20 @@ -16251,7 +16086,7 @@ snapshots: '@sentry/core@10.29.0': {} - '@sentry/nextjs@10.29.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@15.5.11(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.25.12))': + '@sentry/nextjs@10.29.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@15.5.11(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.25.12))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.38.0 @@ -16264,7 +16099,7 @@ snapshots: '@sentry/react': 10.29.0(react@19.2.0) '@sentry/vercel-edge': 10.29.0 '@sentry/webpack-plugin': 4.6.1(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.25.12)) - next: 15.5.11(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next: 15.5.11(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) resolve: 1.22.8 rollup: 4.53.3 stacktrace-parser: 0.1.11 @@ -18280,13 +18115,6 @@ snapshots: tinyrainbow: 3.0.3 vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) - '@vitest/expect@2.1.9': - dependencies: - '@vitest/spy': 2.1.9 - '@vitest/utils': 2.1.9 - chai: 5.3.3 - tinyrainbow: 1.2.0 - '@vitest/expect@3.2.4': dependencies: '@types/chai': 5.2.2 @@ -18304,14 +18132,6 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@2.1.9(vite@7.3.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': - dependencies: - '@vitest/spy': 2.1.9 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) - '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 @@ -18344,10 +18164,6 @@ snapshots: optionalDependencies: vite: 7.3.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) - '@vitest/pretty-format@2.1.9': - dependencies: - tinyrainbow: 1.2.0 - '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 @@ -18356,11 +18172,6 @@ snapshots: dependencies: tinyrainbow: 3.0.3 - '@vitest/runner@2.1.9': - dependencies: - '@vitest/utils': 2.1.9 - pathe: 1.1.2 - '@vitest/runner@3.2.4': dependencies: '@vitest/utils': 3.2.4 @@ -18372,12 +18183,6 @@ snapshots: '@vitest/utils': 4.0.18 pathe: 2.0.3 - '@vitest/snapshot@2.1.9': - dependencies: - '@vitest/pretty-format': 2.1.9 - magic-string: 0.30.21 - pathe: 1.1.2 - '@vitest/snapshot@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 @@ -18390,28 +18195,12 @@ snapshots: magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@2.1.9': - dependencies: - tinyspy: 3.0.2 - '@vitest/spy@3.2.4': dependencies: tinyspy: 4.0.4 '@vitest/spy@4.0.18': {} - '@vitest/ui@2.1.9(vitest@2.1.9)': - dependencies: - '@vitest/utils': 2.1.9 - fflate: 0.8.2 - flatted: 3.3.3 - pathe: 1.1.2 - sirv: 3.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 1.2.0 - vitest: 2.1.9(@types/node@22.19.1)(@vitest/ui@2.1.9)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) - optional: true - '@vitest/ui@3.2.4(vitest@3.2.4)': dependencies: '@vitest/utils': 3.2.4 @@ -18423,12 +18212,6 @@ snapshots: tinyrainbow: 2.0.0 vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.1)(@vitest/ui@3.2.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) - '@vitest/utils@2.1.9': - dependencies: - '@vitest/pretty-format': 2.1.9 - loupe: 3.2.1 - tinyrainbow: 1.2.0 - '@vitest/utils@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 @@ -18836,20 +18619,6 @@ snapshots: transitivePeerDependencies: - supports-color - babel-jest@30.2.0(@babel/core@7.28.4): - dependencies: - '@babel/core': 7.28.4 - '@jest/transform': 30.2.0 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 7.0.1 - babel-preset-jest: 30.2.0(@babel/core@7.28.4) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - optional: true - babel-jest@30.2.0(@babel/core@7.28.5): dependencies: '@babel/core': 7.28.5 @@ -18925,26 +18694,6 @@ snapshots: transitivePeerDependencies: - supports-color - babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.4): - dependencies: - '@babel/core': 7.28.4 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.4) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.4) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.4) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.4) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.4) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.4) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.4) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.4) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.4) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.4) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.4) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.4) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.4) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.4) - optional: true - babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.5): dependencies: '@babel/core': 7.28.5 @@ -18970,13 +18719,6 @@ snapshots: babel-plugin-jest-hoist: 29.6.3 babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5) - babel-preset-jest@30.2.0(@babel/core@7.28.4): - dependencies: - '@babel/core': 7.28.4 - babel-plugin-jest-hoist: 30.2.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4) - optional: true - babel-preset-jest@30.2.0(@babel/core@7.28.5): dependencies: '@babel/core': 7.28.5 @@ -22426,15 +22168,15 @@ snapshots: jose@6.1.3: {} - jotai-minidb@0.0.8(jotai@2.15.1(@babel/core@7.28.4)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0)): + jotai-minidb@0.0.8(jotai@2.15.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0)): dependencies: '@rocicorp/resolver': 1.0.2 idb-keyval: 6.2.2 - jotai: 2.15.1(@babel/core@7.28.4)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0) + jotai: 2.15.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0) - jotai@2.15.1(@babel/core@7.28.4)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0): + jotai@2.15.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0): optionalDependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/template': 7.27.2 '@types/react': 19.2.2 react: 19.2.0 @@ -23356,13 +23098,13 @@ snapshots: neo-async@2.6.2: {} - next-auth@4.24.13(next@15.5.11(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + next-auth@4.24.13(next@15.5.11(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: '@babel/runtime': 7.28.4 '@panva/hkdf': 1.2.1 cookie: 0.7.2 jose: 4.15.9 - next: 15.5.11(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next: 15.5.11(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) oauth: 0.9.15 openid-client: 5.7.1 preact: 10.28.3 @@ -23371,31 +23113,6 @@ snapshots: react-dom: 19.2.0(react@19.2.0) uuid: 8.3.2 - next@15.5.11(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): - dependencies: - '@next/env': 15.5.11 - '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001760 - postcss: 8.4.31 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - styled-jsx: 5.1.6(@babel/core@7.28.4)(react@19.2.0) - optionalDependencies: - '@next/swc-darwin-arm64': 15.5.7 - '@next/swc-darwin-x64': 15.5.7 - '@next/swc-linux-arm64-gnu': 15.5.7 - '@next/swc-linux-arm64-musl': 15.5.7 - '@next/swc-linux-x64-gnu': 15.5.7 - '@next/swc-linux-x64-musl': 15.5.7 - '@next/swc-win32-arm64-msvc': 15.5.7 - '@next/swc-win32-x64-msvc': 15.5.7 - '@opentelemetry/api': 1.9.0 - '@playwright/test': 1.57.0 - sharp: 0.34.5 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - next@15.5.11(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: '@next/env': 15.5.11 @@ -23780,8 +23497,6 @@ snapshots: path-type@4.0.0: {} - pathe@1.1.2: {} - pathe@2.0.3: {} pathval@2.0.1: {} @@ -25138,13 +24853,6 @@ snapshots: dependencies: inline-style-parser: 0.2.4 - styled-jsx@5.1.6(@babel/core@7.28.4)(react@19.2.0): - dependencies: - client-only: 0.0.1 - react: 19.2.0 - optionalDependencies: - '@babel/core': 7.28.4 - styled-jsx@5.1.6(@babel/core@7.28.5)(react@19.2.0): dependencies: client-only: 0.0.1 @@ -25259,14 +24967,10 @@ snapshots: tinypool@1.1.1: {} - tinyrainbow@1.2.0: {} - tinyrainbow@2.0.0: {} tinyrainbow@3.0.3: {} - tinyspy@3.0.2: {} - tinyspy@4.0.4: {} tmpl@1.0.5: {} @@ -25311,12 +25015,12 @@ snapshots: '@ts-graphviz/common': 2.1.5 '@ts-graphviz/core': 2.0.7 - ts-jest@29.4.5(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(esbuild@0.25.12)(jest-util@30.2.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@swc/core@1.12.5)(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3): + ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@swc/core@1.12.5)(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.8 - jest: 30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@swc/core@1.12.5)(@types/node@22.19.1)(typescript@5.9.3)) + jest: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@swc/core@1.12.5)(@types/node@22.19.1)(typescript@5.9.3)) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 @@ -25325,19 +25029,19 @@ snapshots: typescript: 5.9.3 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@jest/transform': 30.2.0 '@jest/types': 30.2.0 - babel-jest: 30.2.0(@babel/core@7.28.4) + babel-jest: 30.2.0(@babel/core@7.28.5) esbuild: 0.25.12 jest-util: 30.2.0 - ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@swc/core@1.12.5)(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3): + ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@swc/core@1.12.5)(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.8 - jest: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@swc/core@1.12.5)(@types/node@22.19.1)(typescript@5.9.3)) + jest: 30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@swc/core@1.12.5)(@types/node@22.19.1)(typescript@5.9.3)) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 @@ -25754,27 +25458,6 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite-node@2.1.9(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): - dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 - pathe: 1.1.2 - vite: 7.3.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - vite-node@3.2.4(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): dependencies: cac: 6.7.14 @@ -25869,45 +25552,6 @@ snapshots: yaml: 2.8.1 optional: true - vitest@2.1.9(@types/node@22.19.1)(@vitest/ui@2.1.9)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): - dependencies: - '@vitest/expect': 2.1.9 - '@vitest/mocker': 2.1.9(vite@7.3.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) - '@vitest/pretty-format': 2.1.9 - '@vitest/runner': 2.1.9 - '@vitest/snapshot': 2.1.9 - '@vitest/spy': 2.1.9 - '@vitest/utils': 2.1.9 - chai: 5.3.3 - debug: 4.4.3 - expect-type: 1.2.2 - magic-string: 0.30.21 - pathe: 1.1.2 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinypool: 1.1.1 - tinyrainbow: 1.2.0 - vite: 7.3.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) - vite-node: 2.1.9(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 22.19.1 - '@vitest/ui': 2.1.9(vitest@2.1.9) - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.19.1)(@vitest/ui@3.2.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 diff --git a/src/routers/cloud-agent-next-router.ts b/src/routers/cloud-agent-next-router.ts index e345e6d88..299005a59 100644 --- a/src/routers/cloud-agent-next-router.ts +++ b/src/routers/cloud-agent-next-router.ts @@ -1,6 +1,5 @@ import 'server-only'; import { TRPCError } from '@trpc/server'; -import { eq as _eq, and as _and } from 'drizzle-orm'; import { baseProcedure, createTRPCRouter } from '@/lib/trpc/init'; import { createCloudAgentNextClient, @@ -31,8 +30,6 @@ import { baseGetSessionNextSchema, baseGetSessionNextOutputSchema, } from './cloud-agent-next-schemas'; -import { db } from '@/lib/drizzle'; -import { cli_sessions_v2 } from '@/db/schema'; import * as z from 'zod'; import { PLATFORM } from '@/lib/integrations/core/constants'; @@ -106,20 +103,6 @@ export const cloudAgentNextRouter = createTRPCRouter({ setupCommands: merged.setupCommands, }); - // Insert cli_sessions_v2 with session IDs - await db - .insert(cli_sessions_v2) - .values({ - session_id: result.kiloSessionId, - kilo_user_id: ctx.user.id, - cloud_agent_session_id: result.cloudAgentSessionId, - version: 0, - }) - .onConflictDoUpdate({ - target: [cli_sessions_v2.session_id, cli_sessions_v2.kilo_user_id], - set: { cloud_agent_session_id: result.cloudAgentSessionId }, - }); - return result; } catch (error) { if (error instanceof ProfileNotFoundError) { diff --git a/src/routers/organizations/organization-cloud-agent-next-router.ts b/src/routers/organizations/organization-cloud-agent-next-router.ts index 621f00b6b..a7ef51c5c 100644 --- a/src/routers/organizations/organization-cloud-agent-next-router.ts +++ b/src/routers/organizations/organization-cloud-agent-next-router.ts @@ -1,6 +1,5 @@ import 'server-only'; import { TRPCError } from '@trpc/server'; -import { eq as _eq, and as _and } from 'drizzle-orm'; import { createTRPCRouter } from '@/lib/trpc/init'; import { createCloudAgentNextClient, @@ -32,8 +31,6 @@ import { baseGetSessionNextSchema, baseGetSessionNextOutputSchema, } from '../cloud-agent-next-schemas'; -import { db } from '@/lib/drizzle'; -import { cli_sessions_v2 } from '@/db/schema'; import * as z from 'zod'; import { PLATFORM } from '@/lib/integrations/core/constants'; @@ -148,24 +145,6 @@ export const organizationCloudAgentNextRouter = createTRPCRouter({ setupCommands: merged.setupCommands, }); - // Insert cli_sessions_v2 with session IDs - await db - .insert(cli_sessions_v2) - .values({ - session_id: result.kiloSessionId, - kilo_user_id: ctx.user.id, - cloud_agent_session_id: result.cloudAgentSessionId, - organization_id: organizationId, - version: 0, - }) - .onConflictDoUpdate({ - target: [cli_sessions_v2.session_id, cli_sessions_v2.kilo_user_id], - set: { - cloud_agent_session_id: result.cloudAgentSessionId, - organization_id: organizationId, - }, - }); - return result; } catch (error) { if (error instanceof ProfileNotFoundError) {