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:
@@ -5,7 +5,6 @@ import type { Config } from '../config.js';
|
||||
import type { Broker } from '../services/broker.js';
|
||||
import type { Session } from '../types/api.js';
|
||||
import { getSetting } from './settings.js';
|
||||
import { requireUser } from '../auth.js';
|
||||
|
||||
const CreateBody = z.object({
|
||||
name: z.string().min(1).max(200).optional(),
|
||||
@@ -31,7 +30,7 @@ export function registerSessionRoutes(
|
||||
config: Config,
|
||||
broker: Broker
|
||||
): void {
|
||||
app.get<{ Params: { id: string } }>(
|
||||
app.get<{ Params: { id: string }; Querystring: { status?: string } }>(
|
||||
'/api/projects/:id/sessions',
|
||||
async (req, reply) => {
|
||||
const project = await sql`SELECT id FROM projects WHERE id = ${req.params.id}`;
|
||||
@@ -39,10 +38,11 @@ export function registerSessionRoutes(
|
||||
reply.code(404);
|
||||
return { error: 'project not found' };
|
||||
}
|
||||
const status = req.query.status === 'archived' ? 'archived' : 'open';
|
||||
const rows = await sql<Session[]>`
|
||||
SELECT id, project_id, name, model, system_prompt, created_at, updated_at
|
||||
SELECT id, project_id, name, model, system_prompt, status, created_at, updated_at
|
||||
FROM sessions
|
||||
WHERE project_id = ${req.params.id}
|
||||
WHERE project_id = ${req.params.id} AND status = ${status}
|
||||
ORDER BY updated_at DESC
|
||||
`;
|
||||
return rows;
|
||||
@@ -81,15 +81,15 @@ export function registerSessionRoutes(
|
||||
const [session] = await tx<Session[]>`
|
||||
INSERT INTO sessions (project_id, name, model, system_prompt)
|
||||
VALUES (${req.params.id}, ${name}, ${model}, ${systemPrompt})
|
||||
RETURNING id, project_id, name, model, system_prompt, created_at, updated_at
|
||||
RETURNING id, project_id, name, model, system_prompt, status, created_at, updated_at
|
||||
`;
|
||||
await tx`
|
||||
INSERT INTO session_panes (session_id, position, kind, state)
|
||||
VALUES (${session!.id}, 0, 'chat', '{}'::jsonb)
|
||||
INSERT INTO chats (session_id, name, status)
|
||||
VALUES (${session!.id}, NULL, 'open')
|
||||
`;
|
||||
return session!;
|
||||
});
|
||||
broker.publishUser(requireUser(req), {
|
||||
broker.publishUser('default', {
|
||||
type: 'session_created',
|
||||
session: row,
|
||||
project_id: row.project_id,
|
||||
@@ -101,7 +101,7 @@ export function registerSessionRoutes(
|
||||
|
||||
app.get<{ Params: { id: string } }>('/api/sessions/:id', async (req, reply) => {
|
||||
const rows = await sql<Session[]>`
|
||||
SELECT id, project_id, name, model, system_prompt, created_at, updated_at
|
||||
SELECT id, project_id, name, model, system_prompt, status, created_at, updated_at
|
||||
FROM sessions WHERE id = ${req.params.id}
|
||||
`;
|
||||
if (rows.length === 0) {
|
||||
@@ -128,7 +128,7 @@ export function registerSessionRoutes(
|
||||
system_prompt = COALESCE(${system_prompt ?? null}, system_prompt),
|
||||
updated_at = clock_timestamp()
|
||||
WHERE id = ${req.params.id}
|
||||
RETURNING id, project_id, name, model, system_prompt, created_at, updated_at
|
||||
RETURNING id, project_id, name, model, system_prompt, status, created_at, updated_at
|
||||
`;
|
||||
if (rows.length === 0) {
|
||||
reply.code(404);
|
||||
@@ -138,6 +138,51 @@ export function registerSessionRoutes(
|
||||
}
|
||||
);
|
||||
|
||||
app.post<{ Params: { id: string } }>(
|
||||
'/api/sessions/:id/archive',
|
||||
async (req, reply) => {
|
||||
const rows = await sql<{ id: string; project_id: string }[]>`
|
||||
UPDATE sessions SET status = 'archived', updated_at = clock_timestamp()
|
||||
WHERE id = ${req.params.id} AND status = 'open'
|
||||
RETURNING id, project_id
|
||||
`;
|
||||
if (rows.length === 0) {
|
||||
reply.code(404);
|
||||
return { error: 'session not found or already archived' };
|
||||
}
|
||||
broker.publishUser('default', {
|
||||
type: 'session_archived',
|
||||
session_id: rows[0]!.id,
|
||||
project_id: rows[0]!.project_id,
|
||||
});
|
||||
reply.code(204);
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
app.post<{ Params: { id: string } }>(
|
||||
'/api/sessions/:id/unarchive',
|
||||
async (req, reply) => {
|
||||
const rows = await sql<Session[]>`
|
||||
UPDATE sessions SET status = 'open', updated_at = clock_timestamp()
|
||||
WHERE id = ${req.params.id} AND status = 'archived'
|
||||
RETURNING id, project_id, name, model, system_prompt, status, created_at, updated_at
|
||||
`;
|
||||
if (rows.length === 0) {
|
||||
reply.code(404);
|
||||
return { error: 'session not found or not archived' };
|
||||
}
|
||||
const session = rows[0]!;
|
||||
broker.publishUser('default', {
|
||||
type: 'session_created',
|
||||
session: session,
|
||||
project_id: session.project_id,
|
||||
});
|
||||
reply.code(200);
|
||||
return session;
|
||||
}
|
||||
);
|
||||
|
||||
app.delete<{ Params: { id: string } }>(
|
||||
'/api/sessions/:id',
|
||||
async (req, reply) => {
|
||||
@@ -150,7 +195,7 @@ export function registerSessionRoutes(
|
||||
return { error: 'not found' };
|
||||
}
|
||||
const project_id = deleted[0]!.project_id;
|
||||
broker.publishUser(requireUser(req), { type: 'session_deleted', session_id: id, project_id });
|
||||
broker.publishUser('default', { type: 'session_deleted', session_id: id, project_id });
|
||||
reply.code(204);
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user