feat: update codex live chat workflow

This commit is contained in:
2026-04-22 20:00:38 +09:00
parent 9e4b70f1f1
commit b0b9980a6c
70 changed files with 5178 additions and 2401 deletions

View File

@@ -14,6 +14,7 @@ const conversationPayloadSchema = z.object({
sessionId: z.string().trim().min(1).max(120),
clientId: z.string().trim().max(120).nullable().optional(),
title: z.string().trim().max(200).nullable().optional(),
chatTypeId: z.string().trim().max(120).nullable().optional(),
contextLabel: z.string().trim().max(200).nullable().optional(),
contextDescription: z.string().trim().max(2000).nullable().optional(),
notifyOffline: z.boolean().optional(),
@@ -32,6 +33,7 @@ export type ChatConversationItem = {
sessionId: string;
clientId: string | null;
title: string;
chatTypeId: string | null;
contextLabel: string | null;
contextDescription: string | null;
notifyOffline: boolean;
@@ -88,6 +90,14 @@ export type ChatConversationActivityLogItem = {
updatedAt: string | null;
};
export type ChatConversationDetailPage = {
messages: StoredChatMessage[];
requests: ChatConversationRequestItem[];
activityLogs: ChatConversationActivityLogItem[];
oldestLoadedMessageId: number | null;
hasOlderMessages: boolean;
};
type ChatConversationRequestStatusPatch = {
requestId: string;
status?: ChatConversationRequestStatus;
@@ -113,6 +123,25 @@ type ChatConversationClientPreference = {
lastReadResponseMessageId: number | null;
};
function normalizeDateTimeValue(value: unknown) {
if (value == null) {
return null;
}
if (value instanceof Date) {
return value.toISOString();
}
const normalized = String(value).trim();
if (!normalized) {
return null;
}
const parsed = new Date(normalized);
return Number.isNaN(parsed.getTime()) ? normalized : parsed.toISOString();
}
function createPreview(text: string) {
const normalized = String(text ?? '').replace(/\s+/g, ' ').trim();
return normalized.length > 140 ? `${normalized.slice(0, 137).trimEnd()}...` : normalized;
@@ -143,6 +172,7 @@ function mapConversationRow(row: Record<string, unknown>): ChatConversationItem
sessionId: String(row.session_id ?? ''),
clientId: row.client_id == null ? null : String(row.client_id),
title: String(row.title ?? '새 대화'),
chatTypeId: row.chat_type_id == null ? null : String(row.chat_type_id),
contextLabel: row.context_label == null ? null : String(row.context_label),
contextDescription: row.context_description == null ? null : String(row.context_description),
notifyOffline: Boolean(row.notify_offline),
@@ -151,11 +181,11 @@ function mapConversationRow(row: Record<string, unknown>): ChatConversationItem
currentJobStatus: row.current_job_status == null ? null : String(row.current_job_status) as ChatConversationItem['currentJobStatus'],
currentJobMessage: row.current_job_message == null ? null : String(row.current_job_message),
currentQueueSize: Number(row.current_queue_size ?? 0),
currentStatusUpdatedAt: row.current_status_updated_at == null ? null : String(row.current_status_updated_at),
currentStatusUpdatedAt: normalizeDateTimeValue(row.current_status_updated_at),
lastMessagePreview: String(row.last_message_preview ?? ''),
createdAt: String(row.created_at ?? ''),
updatedAt: String(row.updated_at ?? ''),
lastMessageAt: row.last_message_at == null ? null : String(row.last_message_at),
createdAt: normalizeDateTimeValue(row.created_at) ?? '',
updatedAt: normalizeDateTimeValue(row.updated_at) ?? '',
lastMessageAt: normalizeDateTimeValue(row.last_message_at),
};
}
@@ -169,7 +199,7 @@ function mapMessageRow(row: Record<string, unknown>): StoredChatMessage {
};
}
function isVisibleConversationMessage(message: StoredChatMessage) {
export function isVisibleConversationMessage(message: StoredChatMessage) {
if (message.author !== 'system') {
return true;
}
@@ -177,6 +207,12 @@ function isVisibleConversationMessage(message: StoredChatMessage) {
return message.text.startsWith(`${CHAT_ACTIVITY_MESSAGE_PREFIX}\n`);
}
function applyVisibleConversationMessageCondition(builder: any) {
builder.whereNot('author', 'system').orWhere((nestedBuilder: any) => {
nestedBuilder.where('author', '=', 'system').andWhere('text', 'like', `${CHAT_ACTIVITY_MESSAGE_PREFIX}\n%`);
});
}
function mapClientPreferenceRow(row: Record<string, unknown>): ChatConversationClientPreference {
return {
sessionId: String(row.session_id ?? ''),
@@ -203,10 +239,10 @@ function mapRequestRow(row: Record<string, unknown>): ChatConversationRequestIte
responseText: String(row.response_text ?? ''),
hasResponse,
canDelete,
createdAt: String(row.created_at ?? ''),
updatedAt: String(row.updated_at ?? ''),
answeredAt: row.answered_at == null ? null : String(row.answered_at),
terminalAt: row.terminal_at == null ? null : String(row.terminal_at),
createdAt: normalizeDateTimeValue(row.created_at) ?? '',
updatedAt: normalizeDateTimeValue(row.updated_at) ?? '',
answeredAt: normalizeDateTimeValue(row.answered_at),
terminalAt: normalizeDateTimeValue(row.terminal_at),
};
}
@@ -560,7 +596,7 @@ async function getLatestPreviewableMessageMap(sessionIds: string[]) {
messageMap.set(sessionId, {
text: String(row.text ?? ''),
createdAt: row.created_at == null ? null : String(row.created_at),
createdAt: normalizeDateTimeValue(row.created_at),
});
}
@@ -594,7 +630,7 @@ async function getLatestRequestPreviewMap(sessionIds: string[]) {
requestMap.set(sessionId, {
text: userText,
createdAt: row.created_at == null ? null : String(row.created_at),
createdAt: normalizeDateTimeValue(row.created_at),
});
}
@@ -672,6 +708,7 @@ export async function ensureChatConversationTables() {
table.string('session_id', 120).primary();
table.string('client_id', 120).nullable().index();
table.string('title', 200).notNullable().defaultTo('새 대화');
table.string('chat_type_id', 120).nullable();
table.string('context_label', 200).nullable();
table.text('context_description').nullable();
table.boolean('notify_offline').notNullable().defaultTo(false);
@@ -690,6 +727,7 @@ export async function ensureChatConversationTables() {
const requiredConversationColumns: Array<[string, (table: any) => void]> = [
['client_id', (table) => table.string('client_id', 120).nullable().index()],
['title', (table) => table.string('title', 200).notNullable().defaultTo('새 대화')],
['chat_type_id', (table) => table.string('chat_type_id', 120).nullable()],
['context_label', (table) => table.string('context_label', 200).nullable()],
['context_description', (table) => table.text('context_description').nullable()],
['notify_offline', (table) => table.boolean('notify_offline').notNullable().defaultTo(false)],
@@ -864,7 +902,6 @@ export async function ensureChatConversationTables() {
}
export async function getChatConversation(sessionId: string, clientId?: string | null) {
await ensureChatConversationTables();
const normalizedSessionId = sessionId.trim();
let row = await db(CHAT_CONVERSATION_TABLE).where({ session_id: normalizedSessionId }).first();
@@ -878,7 +915,7 @@ export async function getChatConversation(sessionId: string, clientId?: string |
shouldClearConversationJobState({
currentRequestId,
currentJobStatus: row.current_job_status == null ? null : String(row.current_job_status) as ChatConversationItem['currentJobStatus'],
currentStatusUpdatedAt: row.current_status_updated_at == null ? null : String(row.current_status_updated_at),
currentStatusUpdatedAt: normalizeDateTimeValue(row.current_status_updated_at),
runtimeActive: isRuntimeRequestActive(currentRequestId),
request: currentRequestId
? await db(CHAT_CONVERSATION_REQUEST_TABLE)
@@ -894,8 +931,8 @@ export async function getChatConversation(sessionId: string, clientId?: string |
status: String(requestRow.status ?? '') as ChatConversationRequestStatus,
responseMessageId: requestRow.response_message_id == null ? null : Number(requestRow.response_message_id),
responseText: String(requestRow.response_text ?? ''),
terminalAt: requestRow.terminal_at == null ? null : String(requestRow.terminal_at),
updatedAt: requestRow.updated_at == null ? null : String(requestRow.updated_at),
terminalAt: normalizeDateTimeValue(requestRow.terminal_at),
updatedAt: normalizeDateTimeValue(requestRow.updated_at),
}
: null,
)
@@ -966,7 +1003,6 @@ export async function getChatConversation(sessionId: string, clientId?: string |
}
export async function createChatConversation(payload: z.input<typeof conversationPayloadSchema>) {
await ensureChatConversationTables();
const parsed = conversationPayloadSchema.parse(payload);
const normalizedClientId = normalizeClientId(parsed.clientId);
const notifyOffline = parsed.notifyOffline ?? true;
@@ -975,6 +1011,7 @@ export async function createChatConversation(payload: z.input<typeof conversatio
session_id: parsed.sessionId,
client_id: normalizedClientId,
title: parsed.title?.trim() || '새 대화',
chat_type_id: parsed.chatTypeId?.trim() || null,
context_label: parsed.contextLabel?.trim() || null,
context_description: parsed.contextDescription?.trim() || null,
notify_offline: notifyOffline,
@@ -1013,12 +1050,12 @@ export async function updateChatConversationContext(
payload: {
title?: string | null;
clientId?: string | null;
chatTypeId?: string | null;
contextLabel?: string | null;
contextDescription?: string | null;
notifyOffline?: boolean | null;
},
) {
await ensureChatConversationTables();
const normalizedClientId = normalizeClientId(payload.clientId);
const current = await db(CHAT_CONVERSATION_TABLE).where({ session_id: sessionId.trim() }).first();
@@ -1031,6 +1068,7 @@ export async function updateChatConversationContext(
.update({
title: payload.title?.trim() || current.title || '새 대화',
client_id: normalizedClientId || current.client_id || null,
chat_type_id: payload.chatTypeId?.trim() || null,
context_label: payload.contextLabel?.trim() || null,
context_description: payload.contextDescription?.trim() || null,
notify_offline:
@@ -1052,32 +1090,44 @@ export async function listChatConversations(
limit = 50,
unreadStateClientId?: string | null,
) {
await ensureChatConversationTables();
const normalizedClientId = normalizeClientId(clientId);
const normalizedUnreadStateClientId = normalizeClientId(unreadStateClientId ?? clientId);
const query = db(CHAT_CONVERSATION_TABLE)
.select('*')
.orderByRaw('COALESCE(last_message_at, updated_at, created_at) DESC NULLS LAST')
.orderByRaw('last_message_at DESC NULLS LAST')
.orderByRaw('updated_at DESC NULLS LAST')
.orderByRaw('created_at DESC NULLS LAST')
.limit(Math.max(1, Math.min(200, Math.round(limit))));
const normalizedLimit = Math.max(1, Math.min(200, Math.round(limit)));
let conversationListScopeClientId = normalizedClientId;
const buildConversationListQuery = (targetClientId?: string | null) => {
const query = db(CHAT_CONVERSATION_TABLE)
.select('*')
.orderByRaw('COALESCE(last_message_at, updated_at, created_at) DESC NULLS LAST')
.orderByRaw('last_message_at DESC NULLS LAST')
.orderByRaw('updated_at DESC NULLS LAST')
.orderByRaw('created_at DESC NULLS LAST')
.limit(normalizedLimit);
if (normalizedClientId) {
query.where((builder) => {
builder
.where({ client_id: normalizedClientId })
.orWhereExists(
db(CHAT_CONVERSATION_CLIENT_TABLE)
.select(db.raw('1'))
.whereRaw(`${CHAT_CONVERSATION_CLIENT_TABLE}.session_id = ${CHAT_CONVERSATION_TABLE}.session_id`)
.andWhere({ client_id: normalizedClientId }),
);
});
if (targetClientId) {
query.where((builder) => {
builder
.where({ client_id: targetClientId })
.orWhereExists(
db(CHAT_CONVERSATION_CLIENT_TABLE)
.select(db.raw('1'))
.whereRaw(`${CHAT_CONVERSATION_CLIENT_TABLE}.session_id = ${CHAT_CONVERSATION_TABLE}.session_id`)
.andWhere({ client_id: targetClientId }),
);
});
}
return query;
};
let rows = await buildConversationListQuery(normalizedClientId);
// Browser storage reset can regenerate client_id and hide existing rooms.
// When that happens, fall back to the recent global list so the user can recover.
if (normalizedClientId && rows.length === 0) {
conversationListScopeClientId = null;
rows = await buildConversationListQuery(null);
}
let rows = await query;
const sessionIds = rows.map((row) => String(row.session_id ?? '')).filter(Boolean);
const currentRequestIds = Array.from(
new Set(rows.map((row) => String(row.current_request_id ?? '').trim()).filter(Boolean)),
@@ -1096,7 +1146,7 @@ export async function listChatConversations(
status: String(requestRow.status ?? '') as ChatConversationRequestStatus,
responseMessageId: requestRow.response_message_id == null ? null : Number(requestRow.response_message_id),
responseText: String(requestRow.response_text ?? ''),
terminalAt: requestRow.terminal_at == null ? null : String(requestRow.terminal_at),
terminalAt: normalizeDateTimeValue(requestRow.terminal_at),
},
]),
);
@@ -1105,7 +1155,7 @@ export async function listChatConversations(
shouldClearConversationJobState({
currentRequestId: String(row.current_request_id ?? ''),
currentJobStatus: row.current_job_status == null ? null : String(row.current_job_status) as ChatConversationItem['currentJobStatus'],
currentStatusUpdatedAt: row.current_status_updated_at == null ? null : String(row.current_status_updated_at),
currentStatusUpdatedAt: normalizeDateTimeValue(row.current_status_updated_at),
runtimeActive: isRuntimeRequestActive(String(row.current_request_id ?? '')),
request:
requestMap.get(`${String(row.session_id ?? '').trim()}:${String(row.current_request_id ?? '').trim()}`) ?? null,
@@ -1149,7 +1199,7 @@ export async function listChatConversations(
current_status_updated_at: db.fn.now(),
updated_at: db.fn.now(),
});
rows = await query.clone();
rows = await buildConversationListQuery(conversationListScopeClientId);
}
}
@@ -1231,7 +1281,6 @@ export async function listChatConversationMessages(
beforeMessageId?: number | null;
} = {},
) {
await ensureChatConversationTables();
const normalizedLimit = Math.max(1, Math.min(1000, Math.round(options.limit ?? 200)));
const normalizedBeforeMessageId =
Number.isFinite(options.beforeMessageId) && (options.beforeMessageId ?? 0) > 0
@@ -1244,20 +1293,226 @@ export async function listChatConversationMessages(
if (normalizedBeforeMessageId !== null) {
builder.where('message_id', '<', normalizedBeforeMessageId);
}
builder.andWhere((visibilityBuilder) => {
applyVisibleConversationMessageCondition(visibilityBuilder);
});
})
.orderBy('message_id', 'desc')
.orderBy('id', 'desc')
.limit(normalizedLimit);
const latestRows = await query;
return latestRows
.reverse()
.map((row: Parameters<typeof mapMessageRow>[0]) => mapMessageRow(row))
.filter((message: ReturnType<typeof mapMessageRow>) => isVisibleConversationMessage(message));
return latestRows.reverse().map((row: Parameters<typeof mapMessageRow>[0]) => mapMessageRow(row));
}
async function listChatConversationActivityLogsByRequestIds(
sessionId: string,
requestIds: string[],
): Promise<ChatConversationActivityLogItem[]> {
const normalizedSessionId = sessionId.trim();
const normalizedRequestIds = Array.from(new Set(requestIds.map((item) => item.trim()).filter(Boolean)));
if (!normalizedSessionId || normalizedRequestIds.length === 0) {
return [];
}
const rows = await db(CHAT_CONVERSATION_ACTIVITY_TABLE)
.select('session_id', 'request_id', 'text', 'line_no', 'created_at')
.where({ session_id: normalizedSessionId })
.whereIn('request_id', normalizedRequestIds)
.orderBy('request_id', 'asc')
.orderBy('line_no', 'asc')
.orderBy('id', 'asc');
const activityMap = new Map<string, ChatConversationActivityLogItem>();
for (const row of rows) {
const requestId = String(row.request_id ?? '').trim();
if (!requestId) {
continue;
}
const existing = activityMap.get(requestId);
if (existing) {
existing.lines.push(String(row.text ?? ''));
existing.updatedAt = normalizeDateTimeValue(row.created_at) ?? existing.updatedAt;
continue;
}
activityMap.set(requestId, {
sessionId: String(row.session_id ?? normalizedSessionId),
requestId,
lines: [String(row.text ?? '')],
updatedAt: normalizeDateTimeValue(row.created_at),
});
}
return normalizedRequestIds
.map((requestId) => activityMap.get(requestId))
.filter(Boolean) as ChatConversationActivityLogItem[];
}
async function resolveConversationRequestCursor(sessionId: string, beforeMessageId: number) {
const normalizedSessionId = sessionId.trim();
const normalizedBeforeMessageId = Math.trunc(beforeMessageId);
if (!normalizedSessionId || normalizedBeforeMessageId <= 0) {
return null;
}
const directRequestRow = await db(CHAT_CONVERSATION_REQUEST_TABLE)
.select('request_id', 'created_at')
.where({ session_id: normalizedSessionId })
.andWhere((builder) => {
builder.where('user_message_id', normalizedBeforeMessageId).orWhere('response_message_id', normalizedBeforeMessageId);
})
.first();
if (directRequestRow) {
return {
requestId: String(directRequestRow.request_id ?? '').trim(),
createdAt: normalizeDateTimeValue(directRequestRow.created_at) ?? '',
};
}
const messageRow = await db(CHAT_CONVERSATION_MESSAGE_TABLE)
.select('client_request_id')
.where({
session_id: normalizedSessionId,
message_id: normalizedBeforeMessageId,
})
.first();
const linkedRequestId = String(messageRow?.client_request_id ?? '').trim();
if (!linkedRequestId) {
return null;
}
const linkedRequestRow = await db(CHAT_CONVERSATION_REQUEST_TABLE)
.select('request_id', 'created_at')
.where({
session_id: normalizedSessionId,
request_id: linkedRequestId,
})
.first();
if (!linkedRequestRow) {
return null;
}
return {
requestId: String(linkedRequestRow.request_id ?? '').trim(),
createdAt: normalizeDateTimeValue(linkedRequestRow.created_at) ?? '',
};
}
export async function listChatConversationDetailPage(
sessionId: string,
options: {
limit?: number;
beforeMessageId?: number | null;
} = {},
): Promise<ChatConversationDetailPage> {
const normalizedSessionId = sessionId.trim();
const conversation = await db(CHAT_CONVERSATION_TABLE).where({ session_id: normalizedSessionId }).first();
const normalizedLimit = Math.max(1, Math.min(100, Math.round(options.limit ?? 6)));
const normalizedBeforeMessageId =
Number.isFinite(options.beforeMessageId) && (options.beforeMessageId ?? 0) > 0
? Math.trunc(options.beforeMessageId as number)
: null;
const requestCursor =
normalizedBeforeMessageId == null
? null
: await resolveConversationRequestCursor(normalizedSessionId, normalizedBeforeMessageId);
const requestRows = await db(CHAT_CONVERSATION_REQUEST_TABLE)
.where({ session_id: normalizedSessionId })
.whereNot('status', 'removed')
.modify((builder) => {
if (!requestCursor) {
return;
}
builder.andWhere((cursorBuilder) => {
cursorBuilder
.where('created_at', '<', requestCursor.createdAt)
.orWhere((sameTimeBuilder) => {
sameTimeBuilder.where('created_at', '=', requestCursor.createdAt).andWhere('request_id', '<', requestCursor.requestId);
});
});
})
.orderBy('created_at', 'desc')
.orderBy('request_id', 'desc')
.limit(normalizedLimit);
const orderedRequestRows = [...requestRows].reverse();
const requests = orderedRequestRows.map((row) => normalizeStaleRequestItem(mapRequestRow(row), conversation));
const requestIds = requests.map((item) => item.requestId.trim()).filter(Boolean);
if (requestIds.length === 0) {
return {
messages: [],
requests,
activityLogs: [],
oldestLoadedMessageId: null,
hasOlderMessages: false,
};
}
const messageRows = await db(CHAT_CONVERSATION_MESSAGE_TABLE)
.select('*')
.where({ session_id: normalizedSessionId })
.whereIn('client_request_id', requestIds)
.andWhere((builder) => {
applyVisibleConversationMessageCondition(builder);
})
.orderBy('message_id', 'asc')
.orderBy('id', 'asc');
const messages = messageRows.map((row: Parameters<typeof mapMessageRow>[0]) => mapMessageRow(row));
const activityLogs = await listChatConversationActivityLogsByRequestIds(normalizedSessionId, requestIds);
const oldestLoadedMessageId =
requests.reduce<number | null>((oldestId, request) => {
const candidateIds = [request.userMessageId, request.responseMessageId].filter(
(value): value is number => typeof value === 'number' && Number.isInteger(value) && value > 0,
);
if (candidateIds.length === 0) {
return oldestId;
}
const nextCandidateId = Math.min(...candidateIds);
return oldestId == null ? nextCandidateId : Math.min(oldestId, nextCandidateId);
}, null) ?? messages[0]?.id ?? null;
const oldestRequest = requests[0] ?? null;
const hasOlderMessages = oldestRequest
? Boolean(
await db(CHAT_CONVERSATION_REQUEST_TABLE)
.where({ session_id: normalizedSessionId })
.whereNot('status', 'removed')
.andWhere((builder) => {
builder
.where('created_at', '<', oldestRequest.createdAt)
.orWhere((sameTimeBuilder) => {
sameTimeBuilder.where('created_at', '=', oldestRequest.createdAt).andWhere('request_id', '<', oldestRequest.requestId);
});
})
.first(),
)
: false;
return {
messages,
requests,
activityLogs,
oldestLoadedMessageId,
hasOlderMessages,
};
}
export async function listChatConversationRequests(sessionId: string, limit = 200) {
await ensureChatConversationTables();
const normalizedSessionId = sessionId.trim();
const conversation = await db(CHAT_CONVERSATION_TABLE).where({ session_id: normalizedSessionId }).first();
@@ -1270,8 +1525,6 @@ export async function listChatConversationRequests(sessionId: string, limit = 20
}
export async function getChatConversationRequest(sessionId: string, requestId: string) {
await ensureChatConversationTables();
const normalizedSessionId = sessionId.trim();
const normalizedRequestId = requestId.trim();
@@ -1313,7 +1566,6 @@ export async function appendChatConversationMessage(
conversationPayload: z.input<typeof conversationPayloadSchema>,
messagePayload: z.input<typeof conversationMessagePayloadSchema>,
) {
await ensureChatConversationTables();
const conversation = conversationPayloadSchema.parse(conversationPayload);
const message = conversationMessagePayloadSchema.parse(messagePayload);
@@ -1353,6 +1605,7 @@ export async function appendChatConversationMessage(
.update({
client_id: normalizeClientId(conversation.clientId) || currentConversation?.client_id || null,
title: nextTitle,
chat_type_id: conversation.chatTypeId?.trim() || currentConversation?.chat_type_id || null,
context_label: conversation.contextLabel?.trim() || currentConversation?.context_label || null,
context_description: conversation.contextDescription?.trim() || currentConversation?.context_description || null,
notify_offline:
@@ -1390,7 +1643,6 @@ export async function appendChatConversationMessage(
}
export async function appendChatConversationActivityLine(sessionId: string, requestId: string, line: string) {
await ensureChatConversationTables();
const normalizedSessionId = sessionId.trim();
const normalizedRequestId = requestId.trim();
const normalizedLine = line.trim();
@@ -1426,7 +1678,6 @@ export async function listChatConversationActivityLogs(
sessionId: string,
limitRequests = 500,
): Promise<ChatConversationActivityLogItem[]> {
await ensureChatConversationTables();
const normalizedSessionId = sessionId.trim();
if (!normalizedSessionId) {
@@ -1469,7 +1720,7 @@ export async function listChatConversationActivityLogs(
if (existing) {
existing.lines.push(String(row.text ?? ''));
existing.updatedAt = row.created_at == null ? existing.updatedAt : String(row.created_at);
existing.updatedAt = normalizeDateTimeValue(row.created_at) ?? existing.updatedAt;
continue;
}
@@ -1477,7 +1728,7 @@ export async function listChatConversationActivityLogs(
sessionId: String(row.session_id ?? normalizedSessionId),
requestId,
lines: [String(row.text ?? '')],
updatedAt: row.created_at == null ? null : String(row.created_at),
updatedAt: normalizeDateTimeValue(row.created_at),
});
}
@@ -1494,8 +1745,6 @@ export async function updateChatConversationJobState(
clear?: boolean;
},
) {
await ensureChatConversationTables();
const current = await db(CHAT_CONVERSATION_TABLE).where({ session_id: sessionId.trim() }).first();
if (!current) {
@@ -1547,8 +1796,6 @@ export async function upsertChatConversationRequest(
responseText?: string | null;
},
) {
await ensureChatConversationTables();
const normalizedSessionId = sessionId.trim();
const normalizedRequestId = payload.requestId.trim();
@@ -1698,7 +1945,7 @@ export async function repairChatConversationRequestLinks(sessionId?: string | nu
author: String(row.author ?? 'codex') as StoredChatMessage['author'],
text: String(row.text ?? ''),
clientRequestId: row.client_request_id == null ? null : String(row.client_request_id),
createdAt: row.created_at == null ? null : String(row.created_at),
createdAt: normalizeDateTimeValue(row.created_at),
}));
for (let index = 0; index < requestRows.length; index += 1) {
@@ -1713,12 +1960,12 @@ export async function repairChatConversationRequestLinks(sessionId?: string | nu
const candidate = selectChatConversationResponseCandidate(
{
requestId,
createdAt: String(requestRow.created_at ?? ''),
createdAt: normalizeDateTimeValue(requestRow.created_at) ?? '',
responseMessageId: requestRow.response_message_id == null ? null : Number(requestRow.response_message_id),
},
nextRequestRow
? {
createdAt: String(nextRequestRow.created_at ?? ''),
createdAt: normalizeDateTimeValue(nextRequestRow.created_at) ?? '',
}
: undefined,
responseMessages,
@@ -1785,8 +2032,6 @@ export async function repairChatConversationRequestLinks(sessionId?: string | nu
}
export async function deleteUnansweredChatConversationRequest(sessionId: string, requestId: string) {
await ensureChatConversationTables();
const normalizedSessionId = sessionId.trim();
const normalizedRequestId = requestId.trim();
const current = await db(CHAT_CONVERSATION_REQUEST_TABLE)
@@ -1866,8 +2111,6 @@ export async function clearAllChatConversationJobStates() {
}
export async function deleteChatConversation(sessionId: string) {
await ensureChatConversationTables();
return db.transaction(async (trx) => {
await trx(CHAT_CONVERSATION_CLIENT_TABLE).where({ session_id: sessionId.trim() }).del();
await trx(CHAT_CONVERSATION_REQUEST_TABLE).where({ session_id: sessionId.trim() }).del();
@@ -1878,7 +2121,6 @@ export async function deleteChatConversation(sessionId: string) {
}
export async function getChatConversationClientPreference(sessionId: string, clientId: string) {
await ensureChatConversationTables();
const row = await db(CHAT_CONVERSATION_CLIENT_TABLE)
.where({
session_id: sessionId.trim(),
@@ -1890,8 +2132,6 @@ export async function getChatConversationClientPreference(sessionId: string, cli
}
export async function listChatConversationOfflineNotificationClientIds(sessionId: string) {
await ensureChatConversationTables();
const rows = await db(CHAT_CONVERSATION_CLIENT_TABLE)
.where({
session_id: sessionId.trim(),
@@ -1905,7 +2145,6 @@ export async function listChatConversationOfflineNotificationClientIds(sessionId
}
export async function upsertChatConversationClientPreference(sessionId: string, clientId: string, notifyOffline: boolean) {
await ensureChatConversationTables();
const normalizedSessionId = sessionId.trim();
const normalizedClientId = clientId.trim();
await db(CHAT_CONVERSATION_CLIENT_TABLE)
@@ -1927,8 +2166,6 @@ export async function upsertChatConversationClientPreference(sessionId: string,
}
export async function markChatConversationResponsesRead(sessionId: string, clientId: string) {
await ensureChatConversationTables();
const normalizedSessionId = sessionId.trim();
const normalizedClientId = clientId.trim();