Update database schema and backend services to properly track session lifecycle: - Sessions now start with 'created' status - Frontend auto-start logic works when status is 'created' - Status transitions to 'ongoing' when session actually starts - Prevents issues with premature round execution 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
265 lines
7.6 KiB
JavaScript
265 lines
7.6 KiB
JavaScript
import express from 'express';
|
|
import collaborativeOrchestrator from '../services/collaborativeOrchestrator.js';
|
|
|
|
const router = express.Router();
|
|
|
|
/**
|
|
* POST /api/collaborate
|
|
* Create a new collaborative session
|
|
*/
|
|
router.post('/', async (req, res) => {
|
|
try {
|
|
const { prompt, documentFormat = 'md', agentCount = 7 } = req.body;
|
|
|
|
if (!prompt || prompt.trim().length === 0) {
|
|
return res.status(400).json({ error: 'Prompt is required' });
|
|
}
|
|
|
|
if (!['md', 'txt'].includes(documentFormat)) {
|
|
return res.status(400).json({ error: 'Document format must be "md" or "txt"' });
|
|
}
|
|
|
|
const sessionId = collaborativeOrchestrator.createSession(
|
|
prompt,
|
|
documentFormat,
|
|
Math.min(agentCount, 7)
|
|
);
|
|
|
|
const session = collaborativeOrchestrator.getSessionDetails(sessionId);
|
|
|
|
res.json({
|
|
sessionId,
|
|
prompt,
|
|
documentFormat,
|
|
status: 'created',
|
|
agents: session.agents.map(a => a.role),
|
|
message: 'Collaborative session created. Start the session to begin collaboration.'
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Error creating collaborative session:', error);
|
|
res.status(500).json({ error: 'Failed to create collaborative session' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* POST /api/collaborate/:id/start
|
|
* Start the collaborative session (Lead Architect creates initial document)
|
|
*/
|
|
router.post('/:id/start', async (req, res) => {
|
|
try {
|
|
const sessionId = parseInt(req.params.id);
|
|
const session = collaborativeOrchestrator.getSession(sessionId);
|
|
|
|
if (!session) {
|
|
return res.status(404).json({ error: 'Session not found' });
|
|
}
|
|
|
|
if (session.status !== 'created') {
|
|
return res.status(400).json({ error: 'Session has already been started or is no longer available' });
|
|
}
|
|
|
|
// Start the session asynchronously
|
|
res.json({
|
|
sessionId,
|
|
status: 'starting',
|
|
message: 'Session is starting. Initial document is being created...'
|
|
});
|
|
|
|
// Start asynchronously without waiting
|
|
collaborativeOrchestrator.startCollaborativeSession(sessionId).catch(error => {
|
|
console.error('Error starting collaborative session:', error);
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Error starting collaborative session:', error);
|
|
res.status(500).json({ error: 'Failed to start collaborative session' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* POST /api/collaborate/:id/round
|
|
* Run the next round of reviews
|
|
*/
|
|
router.post('/:id/round', async (req, res) => {
|
|
try {
|
|
const sessionId = parseInt(req.params.id);
|
|
const session = collaborativeOrchestrator.getSession(sessionId);
|
|
|
|
if (!session) {
|
|
return res.status(404).json({ error: 'Session not found' });
|
|
}
|
|
|
|
if (session.status !== 'ongoing') {
|
|
return res.status(400).json({ error: 'Session is not in ongoing status' });
|
|
}
|
|
|
|
const activeSession = collaborativeOrchestrator.activeSessions.get(sessionId);
|
|
if (!activeSession?.started) {
|
|
return res.status(400).json({ error: 'Session has not been started yet' });
|
|
}
|
|
|
|
// Run round asynchronously
|
|
res.json({
|
|
sessionId,
|
|
roundNumber: activeSession.currentRound + 1,
|
|
status: 'running',
|
|
message: 'Review round in progress...'
|
|
});
|
|
|
|
// Run asynchronously without waiting
|
|
collaborativeOrchestrator.runRound(sessionId).catch(error => {
|
|
console.error('Error running round:', error);
|
|
collaborativeOrchestrator.broadcast(sessionId, {
|
|
type: 'round_error',
|
|
sessionId,
|
|
error: error.message
|
|
});
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Error running round:', error);
|
|
res.status(500).json({ error: 'Failed to run round' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/collaborate/:id
|
|
* Get session details with full history and current document
|
|
*/
|
|
router.get('/:id', (req, res) => {
|
|
try {
|
|
const sessionId = parseInt(req.params.id);
|
|
const session = collaborativeOrchestrator.getSessionDetails(sessionId);
|
|
|
|
if (!session) {
|
|
return res.status(404).json({ error: 'Session not found' });
|
|
}
|
|
|
|
res.json({
|
|
sessionId: session.id,
|
|
initialPrompt: session.initial_prompt,
|
|
documentFormat: session.document_format,
|
|
status: session.status,
|
|
createdAt: session.created_at,
|
|
completedAt: session.completed_at,
|
|
currentRound: session.currentRound,
|
|
agents: session.agents.map(a => a.role),
|
|
currentDocument: session.currentDocument,
|
|
documentVersionCount: session.versions.length,
|
|
conversationHistory: session.conversationHistory,
|
|
versions: session.versions.map(v => ({
|
|
versionNumber: v.version_number,
|
|
modifiedBy: v.modified_by,
|
|
modificationReason: v.modification_reason,
|
|
roundNumber: v.round_number,
|
|
createdAt: v.created_at
|
|
}))
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Error fetching session details:', error);
|
|
res.status(500).json({ error: 'Failed to fetch session details' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/collaborate/:id/document
|
|
* Get the current document
|
|
*/
|
|
router.get('/:id/document', (req, res) => {
|
|
try {
|
|
const sessionId = parseInt(req.params.id);
|
|
const session = collaborativeOrchestrator.getSession(sessionId);
|
|
|
|
if (!session) {
|
|
return res.status(404).json({ error: 'Session not found' });
|
|
}
|
|
|
|
const activeSession = collaborativeOrchestrator.activeSessions.get(sessionId);
|
|
const currentDocument = activeSession?.currentDocument || '';
|
|
|
|
// Determine content type based on format
|
|
const contentType = session.document_format === 'md'
|
|
? 'text/markdown; charset=utf-8'
|
|
: 'text/plain; charset=utf-8';
|
|
|
|
res.set('Content-Type', contentType);
|
|
res.send(currentDocument);
|
|
|
|
} catch (error) {
|
|
console.error('Error fetching document:', error);
|
|
res.status(500).json({ error: 'Failed to fetch document' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/collaborate/:id/versions/:versionNumber
|
|
* Get a specific document version
|
|
*/
|
|
router.get('/:id/versions/:versionNumber', (req, res) => {
|
|
try {
|
|
const sessionId = parseInt(req.params.id);
|
|
const versionNumber = parseInt(req.params.versionNumber);
|
|
|
|
const session = collaborativeOrchestrator.getSession(sessionId);
|
|
if (!session) {
|
|
return res.status(404).json({ error: 'Session not found' });
|
|
}
|
|
|
|
const versions = collaborativeOrchestrator.getDocumentVersions(sessionId);
|
|
const version = versions.find(v => v.version_number === versionNumber);
|
|
|
|
if (!version) {
|
|
return res.status(404).json({ error: 'Version not found' });
|
|
}
|
|
|
|
const contentType = session.document_format === 'md'
|
|
? 'text/markdown; charset=utf-8'
|
|
: 'text/plain; charset=utf-8';
|
|
|
|
res.set('Content-Type', contentType);
|
|
res.json({
|
|
versionNumber: version.version_number,
|
|
modifiedBy: version.modified_by,
|
|
modificationReason: version.modification_reason,
|
|
roundNumber: version.round_number,
|
|
createdAt: version.created_at,
|
|
content: version.content
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Error fetching version:', error);
|
|
res.status(500).json({ error: 'Failed to fetch version' });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* POST /api/collaborate/:id/complete
|
|
* Complete the collaborative session
|
|
*/
|
|
router.post('/:id/complete', (req, res) => {
|
|
try {
|
|
const sessionId = parseInt(req.params.id);
|
|
const session = collaborativeOrchestrator.getSession(sessionId);
|
|
|
|
if (!session) {
|
|
return res.status(404).json({ error: 'Session not found' });
|
|
}
|
|
|
|
collaborativeOrchestrator.completeSession(sessionId);
|
|
|
|
res.json({
|
|
sessionId,
|
|
status: 'completed',
|
|
message: 'Session completed successfully'
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Error completing session:', error);
|
|
res.status(500).json({ error: 'Failed to complete session' });
|
|
}
|
|
});
|
|
|
|
export default router;
|