batch4.1-5.1: dedup audit, archive 400 fix, sidebar Delete, landing-page enrichment, auto-name tool-call fix

- Fastify global empty-JSON-body parser fixes archive/unarchive/stop 400s
- Removed redundant local sessionEvents.emit at all 5+2 sites with server-side WS publishers; added dedupe guards in useSidebar/Workspace/Project handlers
- Sidebar session right-click adds Delete (destructive) with confirm Dialog
- Session.tsx navigates away on session_deleted/session_archived for the active session
- SessionLandingPage chat rows show message_count, effective_context_tokens, last_message_preview via LATERAL joins on GET /api/sessions/:id/chats
- Workspace.tsx pane drag-to-reorder using native HTML5 events (no new deps)
- CompactCard: Copy toast, Send-to-chat with target chat name, empty-state in share popover, Re-run button
- auto_name.ts: filter count gate and assistant-fetch by content <> '' so tool-call assistant rows don't trip the once-and-only-once guard
- Adds CLAUDE.md and apps/web/src/lib/format.ts

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-15 23:36:01 +00:00
parent c35ec65fc4
commit 051f3b96ae
15 changed files with 451 additions and 90 deletions

View File

@@ -37,11 +37,17 @@ export function Project() {
if (event.type === 'session_archived' && event.project_id === id) {
setArchivedSessions((prev) => {
if (!prev) return prev;
if (prev.some((s) => s.id === event.session_id)) return prev;
const session = sessions?.find((s) => s.id === event.session_id);
if (!session) return prev;
return [{ ...session, status: 'archived' as const }, ...prev];
});
}
if (event.type === 'session_deleted' && event.project_id === id) {
setArchivedSessions((prev) =>
prev ? prev.filter((s) => s.id !== event.session_id) : prev
);
}
});
}, [id, sessions]);
@@ -50,7 +56,7 @@ export function Project() {
setCreating(true);
try {
const s = await create({});
sessionEvents.emit({ type: 'session_created', session: s, project_id: id });
// Server publishes session_created via WS; let useUserEvents deliver it.
navigate(`/session/${s.id}`);
} finally {
setCreating(false);
@@ -112,11 +118,7 @@ export function Project() {
onClick={async () => {
try {
await remove(s.id);
sessionEvents.emit({
type: 'session_deleted',
session_id: s.id,
project_id: id!,
});
// Server publishes session_deleted via WS.
} catch (err) {
toast.error(
err instanceof Error ? err.message : 'failed to delete session'