Implement session management to maintain conversation history and entity context across page refreshes. Sessions are stored in-memory and include: - Conversation history (stored server-side, restored on reconnect) - Entity snapshots synced before each message for LLM context - Auto-injection of diagram state into Claude's system prompt Key changes: - Add session store service with create/resume/sync/clear operations - Add session API endpoints (/api/session/*) - Update Claude API to inject entity context and manage history - Update ChatPanel to initialize sessions and sync entities - Add debug endpoint for inspecting session state 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
145 lines
3.5 KiB
JavaScript
145 lines
3.5 KiB
JavaScript
import { Router } from "express";
|
|
import {
|
|
createSession,
|
|
getSession,
|
|
findSessionByDiagram,
|
|
syncEntities,
|
|
addMessage,
|
|
clearHistory,
|
|
deleteSession,
|
|
getStats
|
|
} from "../services/sessionStore.js";
|
|
|
|
const router = Router();
|
|
|
|
/**
|
|
* GET /api/session/debug/stats
|
|
* Get session statistics (for debugging)
|
|
* Query params:
|
|
* - details=true: Include full entity and conversation data
|
|
* NOTE: Must be before /:id routes to avoid matching "debug" as an id
|
|
*/
|
|
router.get("/debug/stats", (req, res) => {
|
|
const includeDetails = req.query.details === 'true';
|
|
const stats = getStats(includeDetails);
|
|
console.log('[Session Debug] Stats requested:', JSON.stringify(stats, null, 2));
|
|
res.json(stats);
|
|
});
|
|
|
|
/**
|
|
* POST /api/session/create
|
|
* Create a new session or return existing one for a diagram
|
|
*/
|
|
router.post("/create", (req, res) => {
|
|
const { diagramId } = req.body;
|
|
|
|
if (!diagramId) {
|
|
return res.status(400).json({ error: "diagramId is required" });
|
|
}
|
|
|
|
// Check for existing session
|
|
let session = findSessionByDiagram(diagramId);
|
|
if (session) {
|
|
console.log(`[Session] Resuming existing session ${session.id} for diagram ${diagramId} (${session.conversationHistory.length} messages, ${session.entities.length} entities)`);
|
|
return res.json({
|
|
session,
|
|
isNew: false
|
|
});
|
|
}
|
|
|
|
// Create new session
|
|
session = createSession(diagramId);
|
|
console.log(`[Session] Created new session ${session.id} for diagram ${diagramId}`);
|
|
res.json({
|
|
session,
|
|
isNew: true
|
|
});
|
|
});
|
|
|
|
/**
|
|
* GET /api/session/:id
|
|
* Get session details including history
|
|
*/
|
|
router.get("/:id", (req, res) => {
|
|
const session = getSession(req.params.id);
|
|
|
|
if (!session) {
|
|
return res.status(404).json({ error: "Session not found" });
|
|
}
|
|
|
|
res.json({ session });
|
|
});
|
|
|
|
/**
|
|
* PUT /api/session/:id/sync
|
|
* Sync entities from client to server
|
|
*/
|
|
router.put("/:id/sync", (req, res) => {
|
|
const { entities } = req.body;
|
|
|
|
if (!entities || !Array.isArray(entities)) {
|
|
return res.status(400).json({ error: "entities array is required" });
|
|
}
|
|
|
|
const session = syncEntities(req.params.id, entities);
|
|
|
|
if (!session) {
|
|
return res.status(404).json({ error: "Session not found" });
|
|
}
|
|
|
|
console.log(`[Session ${req.params.id}] Synced ${entities.length} entities:`,
|
|
entities.map(e => `${e.text || '(no label)'} (${e.template})`).join(', ') || 'none');
|
|
|
|
res.json({ success: true, entityCount: entities.length });
|
|
});
|
|
|
|
/**
|
|
* POST /api/session/:id/message
|
|
* Add a message to history (used after successful Claude response)
|
|
*/
|
|
router.post("/:id/message", (req, res) => {
|
|
const { role, content, toolResults } = req.body;
|
|
|
|
if (!role || !content) {
|
|
return res.status(400).json({ error: "role and content are required" });
|
|
}
|
|
|
|
const session = addMessage(req.params.id, { role, content, toolResults });
|
|
|
|
if (!session) {
|
|
return res.status(404).json({ error: "Session not found" });
|
|
}
|
|
|
|
res.json({ success: true, messageCount: session.conversationHistory.length });
|
|
});
|
|
|
|
/**
|
|
* DELETE /api/session/:id/history
|
|
* Clear conversation history
|
|
*/
|
|
router.delete("/:id/history", (req, res) => {
|
|
const session = clearHistory(req.params.id);
|
|
|
|
if (!session) {
|
|
return res.status(404).json({ error: "Session not found" });
|
|
}
|
|
|
|
res.json({ success: true });
|
|
});
|
|
|
|
/**
|
|
* DELETE /api/session/:id
|
|
* Delete a session entirely
|
|
*/
|
|
router.delete("/:id", (req, res) => {
|
|
const deleted = deleteSession(req.params.id);
|
|
|
|
if (!deleted) {
|
|
return res.status(404).json({ error: "Session not found" });
|
|
}
|
|
|
|
res.json({ success: true });
|
|
});
|
|
|
|
export default router;
|