mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-06-13 01:22:06 +08:00
feat(chat): unify session gateway with stable IDs and a single WS protocol
The frontend previously juggled placeholder IDs, provider-native IDs, and session_created handoffs, which caused race conditions and provider-specific branching. This introduces app-allocated session IDs, a chat run registry with event replay, delta sidebar updates, and one kind-based websocket contract so the UI can treat every provider the same while JSONL remains the source of truth.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import fsp from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
@@ -11,6 +12,12 @@ import type {
|
||||
} from '@/shared/types.js';
|
||||
import { AppError } from '@/shared/utils.js';
|
||||
|
||||
type CreateAppSessionResult = {
|
||||
sessionId: string;
|
||||
provider: LLMProvider;
|
||||
projectPath: string;
|
||||
};
|
||||
|
||||
type ArchivedSessionListItem = {
|
||||
sessionId: string;
|
||||
provider: LLMProvider;
|
||||
@@ -89,12 +96,43 @@ export const sessionsService = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches persisted history by session id.
|
||||
* Allocates a stable app-facing session id before any provider run happens.
|
||||
*
|
||||
* This is the entry point of the session gateway: the frontend calls this
|
||||
* (via `POST /api/providers/sessions`) when the user starts a brand-new
|
||||
* chat, navigates to the returned id immediately, and the id never changes
|
||||
* for the lifetime of the conversation. The provider-native id is mapped to
|
||||
* this row later, when the provider runtime announces it mid-run.
|
||||
*/
|
||||
createAppSession(provider: LLMProvider, projectPath: string): CreateAppSessionResult {
|
||||
const normalizedProjectPath = projectPath.trim();
|
||||
if (!normalizedProjectPath) {
|
||||
throw new AppError('projectPath is required.', {
|
||||
code: 'PROJECT_PATH_REQUIRED',
|
||||
statusCode: 400,
|
||||
});
|
||||
}
|
||||
|
||||
const sessionId = randomUUID();
|
||||
sessionsDb.createAppSession(sessionId, provider, normalizedProjectPath);
|
||||
|
||||
return {
|
||||
sessionId,
|
||||
provider,
|
||||
projectPath: normalizedProjectPath,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches persisted history by app session id.
|
||||
*
|
||||
* Provider and provider-specific lookup hints are resolved from the indexed
|
||||
* session metadata in the database.
|
||||
* session metadata in the database. The provider adapter receives the
|
||||
* provider-native session id (the one written into transcripts on disk),
|
||||
* and every returned message is remapped back to the app session id so
|
||||
* provider ids never reach the frontend.
|
||||
*/
|
||||
fetchHistory(
|
||||
async fetchHistory(
|
||||
sessionId: string,
|
||||
options: Pick<FetchHistoryOptions, 'limit' | 'offset'> = {},
|
||||
): Promise<FetchHistoryResult> {
|
||||
@@ -106,12 +144,33 @@ export const sessionsService = {
|
||||
});
|
||||
}
|
||||
|
||||
// App-created sessions that never produced a provider transcript yet
|
||||
// (e.g. first message still streaming) simply have no history.
|
||||
if (!session.provider_session_id) {
|
||||
return {
|
||||
messages: [],
|
||||
total: 0,
|
||||
hasMore: false,
|
||||
offset: options.offset ?? 0,
|
||||
limit: options.limit ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
const provider = session.provider as LLMProvider;
|
||||
return providerRegistry.resolveProvider(provider).sessions.fetchHistory(sessionId, {
|
||||
const result = await providerRegistry.resolveProvider(provider).sessions.fetchHistory(sessionId, {
|
||||
limit: options.limit ?? null,
|
||||
offset: options.offset ?? 0,
|
||||
projectPath: session.project_path ?? '',
|
||||
providerSessionId: session.provider_session_id,
|
||||
});
|
||||
|
||||
return {
|
||||
...result,
|
||||
messages: result.messages.map((message) => ({
|
||||
...message,
|
||||
sessionId,
|
||||
})),
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user