batch4: chats-in-sessions, force-send, /compact, right-rail file browser
Session 1:N Chat data model with backfill. Workspace switches to client-side multi-tab pane management. Right-rail file browser with float-over viewer and click-drag line selection replaces FileBrowserPane. Adds /compact streaming summarizer (respects compact markers in context builder), force-send (cancels in-flight, persists partial as 'cancelled', awaits cancellation completion via deferred Promise + 5s timeout), message queue, stop generation, chat auto-rename, session archive/unarchive with Closed Sessions section on repo landing page. CHECK constraints on sessions.status, messages.role, messages.status with KEEP IN SYNC comments tying to MESSAGE_ROLES / MESSAGE_STATUSES const arrays. Deletes dead pane routes/hook and the api.panes.* client block. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,14 +2,12 @@ import type {
|
||||
Project,
|
||||
AvailableProject,
|
||||
Session,
|
||||
Chat,
|
||||
Message,
|
||||
ModelInfo,
|
||||
SidebarResponse,
|
||||
ListDirResult,
|
||||
ViewFileResult,
|
||||
Pane,
|
||||
PaneCreateRequest,
|
||||
PaneUpdateRequest,
|
||||
} from './types';
|
||||
|
||||
export class ApiError extends Error {
|
||||
@@ -61,8 +59,8 @@ export const api = {
|
||||
},
|
||||
|
||||
sessions: {
|
||||
listForProject: (projectId: string) =>
|
||||
request<Session[]>(`/api/projects/${projectId}/sessions`),
|
||||
listForProject: (projectId: string, status?: 'open' | 'archived') =>
|
||||
request<Session[]>(`/api/projects/${projectId}/sessions${status ? `?status=${status}` : ''}`),
|
||||
create: (
|
||||
projectId: string,
|
||||
body: { name?: string; model?: string; system_prompt?: string }
|
||||
@@ -82,22 +80,54 @@ export const api = {
|
||||
}),
|
||||
remove: (id: string) =>
|
||||
request<void>(`/api/sessions/${id}`, { method: 'DELETE' }),
|
||||
archive: (id: string) =>
|
||||
request<void>(`/api/sessions/${id}/archive`, { method: 'POST' }),
|
||||
unarchive: (id: string) =>
|
||||
request<Session>(`/api/sessions/${id}/unarchive`, { method: 'POST' }),
|
||||
},
|
||||
|
||||
chats: {
|
||||
listForSession: (sessionId: string) =>
|
||||
request<Chat[]>(`/api/sessions/${sessionId}/chats`),
|
||||
create: (sessionId: string, body?: { name?: string }) =>
|
||||
request<Chat>(`/api/sessions/${sessionId}/chats`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body ?? {}),
|
||||
}),
|
||||
update: (chatId: string, body: { name?: string; status?: 'open' | 'closed' }) =>
|
||||
request<Chat>(`/api/chats/${chatId}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(body),
|
||||
}),
|
||||
remove: (chatId: string) =>
|
||||
request<void>(`/api/chats/${chatId}`, { method: 'DELETE' }),
|
||||
messages: (chatId: string) =>
|
||||
request<Message[]>(`/api/chats/${chatId}/messages`),
|
||||
compact: (chatId: string) =>
|
||||
request<{ compact_message_id: string }>(`/api/chats/${chatId}/compact`, { method: 'POST' }),
|
||||
stop: (chatId: string) =>
|
||||
request<{ stopped: boolean }>(`/api/chats/${chatId}/stop`, { method: 'POST' }),
|
||||
forceSend: (chatId: string, content: string) =>
|
||||
request<{ user_message_id: string; assistant_message_id: string }>(
|
||||
`/api/chats/${chatId}/force_send`,
|
||||
{ method: 'POST', body: JSON.stringify({ content }) }
|
||||
),
|
||||
},
|
||||
|
||||
messages: {
|
||||
list: (sessionId: string) =>
|
||||
request<Message[]>(`/api/sessions/${sessionId}/messages`),
|
||||
send: (sessionId: string, content: string) =>
|
||||
send: (chatId: string, content: string) =>
|
||||
request<{ user_message_id: string; assistant_message_id: string }>(
|
||||
`/api/sessions/${sessionId}/messages`,
|
||||
`/api/chats/${chatId}/messages`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ content }),
|
||||
}
|
||||
),
|
||||
regenerate: (sessionId: string, messageId: string) =>
|
||||
regenerate: (chatId: string, messageId: string) =>
|
||||
request<{ assistant_message_id: string }>(
|
||||
`/api/sessions/${sessionId}/messages/${messageId}/regenerate`,
|
||||
`/api/chats/${chatId}/messages/${messageId}/regenerate`,
|
||||
{ method: 'POST' }
|
||||
),
|
||||
},
|
||||
@@ -116,21 +146,4 @@ export const api = {
|
||||
sidebar: {
|
||||
get: () => request<SidebarResponse>('/api/sidebar'),
|
||||
},
|
||||
|
||||
panes: {
|
||||
getForSession: (sessionId: string) =>
|
||||
request<{ panes: Pane[] }>(`/api/sessions/${sessionId}/panes`),
|
||||
create: (sessionId: string, body: PaneCreateRequest) =>
|
||||
request<Pane>(`/api/sessions/${sessionId}/panes`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
}),
|
||||
update: (id: string, body: PaneUpdateRequest) =>
|
||||
request<Pane>(`/api/panes/${id}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(body),
|
||||
}),
|
||||
remove: (id: string) =>
|
||||
request<void>(`/api/panes/${id}`, { method: 'DELETE' }),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -11,18 +11,33 @@ export interface AvailableProject {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type SessionStatus = 'open' | 'archived';
|
||||
|
||||
export interface Session {
|
||||
id: string;
|
||||
project_id: string;
|
||||
name: string;
|
||||
model: string;
|
||||
system_prompt: string;
|
||||
status: SessionStatus;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export type MessageRole = 'user' | 'assistant' | 'tool';
|
||||
export type MessageStatus = 'streaming' | 'complete' | 'failed';
|
||||
export type ChatStatus = 'open' | 'closed';
|
||||
|
||||
export interface Chat {
|
||||
id: string;
|
||||
session_id: string;
|
||||
name: string | null;
|
||||
status: ChatStatus;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export type MessageRole = 'user' | 'assistant' | 'tool' | 'system';
|
||||
export type MessageStatus = 'streaming' | 'complete' | 'failed' | 'cancelled';
|
||||
export type MessageKind = 'message' | 'compact';
|
||||
|
||||
export interface ToolCall {
|
||||
id: string;
|
||||
@@ -40,8 +55,10 @@ export interface ToolResult {
|
||||
export interface Message {
|
||||
id: string;
|
||||
session_id: string;
|
||||
chat_id: string;
|
||||
role: MessageRole;
|
||||
content: string;
|
||||
kind: MessageKind;
|
||||
tool_calls: ToolCall[] | null;
|
||||
tool_results: ToolResult | null;
|
||||
status: MessageStatus;
|
||||
@@ -127,14 +144,25 @@ export interface PaneUpdateRequest {
|
||||
position?: number;
|
||||
}
|
||||
|
||||
export type WorkspacePaneKind = 'chat' | 'terminal' | 'agent' | 'empty';
|
||||
|
||||
export interface WorkspacePane {
|
||||
id: string;
|
||||
kind: WorkspacePaneKind;
|
||||
chatId?: string;
|
||||
chatIds: string[];
|
||||
activeChatIdx: number;
|
||||
}
|
||||
|
||||
export type WsFrame =
|
||||
| { type: 'snapshot'; messages: Message[] }
|
||||
| { type: 'message_started'; message_id: string; role: MessageRole }
|
||||
| { type: 'delta'; message_id: string; content: string }
|
||||
| { type: 'tool_call'; message_id: string; tool_call: ToolCall }
|
||||
| { type: 'message_started'; message_id: string; chat_id?: string; role: MessageRole }
|
||||
| { type: 'delta'; message_id: string; chat_id?: string; content: string }
|
||||
| { type: 'tool_call'; message_id: string; chat_id?: string; tool_call: ToolCall }
|
||||
| {
|
||||
type: 'tool_result';
|
||||
tool_message_id: string;
|
||||
chat_id?: string;
|
||||
tool_call_id: string;
|
||||
output: unknown;
|
||||
truncated: boolean;
|
||||
@@ -143,12 +171,14 @@ export type WsFrame =
|
||||
| {
|
||||
type: 'message_complete';
|
||||
message_id: string;
|
||||
chat_id?: string;
|
||||
tokens_used?: number | null;
|
||||
ctx_used?: number | null;
|
||||
ctx_max?: number | null;
|
||||
started_at?: string | null;
|
||||
finished_at?: string | null;
|
||||
}
|
||||
| { type: 'messages_deleted'; message_ids: string[] }
|
||||
| { type: 'session_renamed'; session_id: string; name: string }
|
||||
| { type: 'error'; message_id?: string; error: string };
|
||||
| { type: 'messages_deleted'; message_ids: string[]; chat_id?: string }
|
||||
| { type: 'session_renamed'; session_id: string; name: string; chat_id?: string }
|
||||
| { type: 'chat_renamed'; chat_id: string; name: string }
|
||||
| { type: 'error'; message_id?: string; chat_id?: string; error: string };
|
||||
|
||||
Reference in New Issue
Block a user