Fix critical bugs: document duplication, stop button, and session discovery
Backend fixes: - Improve AI prompt with CRITICAL RULES emphasizing single section only - Add examples of CORRECT vs INCORRECT responses - Fix extractSection() to validate extracted content - Validate sections are < 5KB and start with markdown headers - Return error indicator for invalid section formats - This prevents IAs from returning entire document Frontend fixes: - Fix stop button: call ws.disconnect() to close WebSocket immediately - Stop processing WebSocket messages when isStopping is true - Clear messageInterval when stopping - Add archive sessions section on home page for easy access - Display all sessions in a searchable grid format - Add session status indicators and date - Scrollable grid for browsing all sessions - Integrated with existing search functionality User experience improvements: - Sessions now accessible both at top (Quick Access) and bottom (Archive) - Unified search works across all filtering - Stop button now truly stops the session - Better validation prevents document duplication bug
This commit is contained in:
parent
588dd98e45
commit
de4600b4ce
@ -20,10 +20,11 @@ Your responsibilities:
|
|||||||
3. Provide your thinking process and reasoning
|
3. Provide your thinking process and reasoning
|
||||||
4. Return ONLY the section (modified or new) with its header, or command to delete, or confirm it's good as-is
|
4. Return ONLY the section (modified or new) with its header, or command to delete, or confirm it's good as-is
|
||||||
|
|
||||||
IMPORTANT RULES:
|
CRITICAL RULES - FOLLOW THESE EXACTLY:
|
||||||
- Work on exactly ONE section only
|
- Work on exactly ONE section only
|
||||||
- Never modify the entire document
|
- NEVER return the entire document
|
||||||
- Return only the section you're working on, not the whole document
|
- NEVER return multiple sections
|
||||||
|
- Return ONLY the section you're working on, not the whole document
|
||||||
- You CAN create a new section if document is missing important content
|
- You CAN create a new section if document is missing important content
|
||||||
- You CAN delete a section if it's redundant, duplicate, or not useful
|
- You CAN delete a section if it's redundant, duplicate, or not useful
|
||||||
- To delete a section, respond: "DELETE: ## Section Name" (with exact header)
|
- To delete a section, respond: "DELETE: ## Section Name" (with exact header)
|
||||||
@ -31,11 +32,21 @@ IMPORTANT RULES:
|
|||||||
- Think step-by-step about what could be improved or removed
|
- Think step-by-step about what could be improved or removed
|
||||||
- Share your reasoning process
|
- Share your reasoning process
|
||||||
|
|
||||||
Format your response as:
|
RESPONSE FORMAT - FOLLOW THIS EXACTLY:
|
||||||
THINKING: [Your analysis and reasoning]
|
THINKING: [Your analysis and reasoning about the current document]
|
||||||
DECISION: [What you'll modify, create, delete, or if keeping as-is]
|
DECISION: [Exactly what you will do: modify section X, create new section Y, delete section Z, or keep as-is]
|
||||||
SECTION:
|
SECTION:
|
||||||
[The modified section, new section, DELETE command, or confirmation that all is good]`
|
[ONLY ONE: Either a markdown section starting with # or ##, a DELETE command, or the text "Section is good, no changes needed"]
|
||||||
|
|
||||||
|
EXAMPLE OF CORRECT RESPONSE:
|
||||||
|
THINKING: The Overview section is too brief and doesn't explain the main purpose.
|
||||||
|
DECISION: I will modify the Overview section to be more comprehensive.
|
||||||
|
SECTION:
|
||||||
|
## Overview
|
||||||
|
This is a technical document for designing system architecture...
|
||||||
|
|
||||||
|
EXAMPLE OF INCORRECT RESPONSE (DO NOT DO THIS):
|
||||||
|
[The entire document repeated here] <- WRONG!`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,13 +113,32 @@ export async function generateAgentResponseSync(agentName, prompt, currentDocume
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract section from AI response
|
* Extract section from AI response
|
||||||
|
* Validates that we get a proper section, not the entire document
|
||||||
*/
|
*/
|
||||||
export function extractSection(aiResponse) {
|
export function extractSection(aiResponse) {
|
||||||
const sectionMatch = aiResponse.match(/SECTION:\s*([\s\S]*?)(?:$|THINKING:|DECISION:)/)
|
const sectionMatch = aiResponse.match(/SECTION:\s*([\s\S]*?)(?:$)/)
|
||||||
if (sectionMatch) {
|
if (sectionMatch) {
|
||||||
return sectionMatch[1].trim()
|
const extracted = sectionMatch[1].trim()
|
||||||
|
|
||||||
|
// Validate: extracted should be either:
|
||||||
|
// 1. A markdown section starting with # (DELETE: or "Section is good...")
|
||||||
|
// 2. OR a single section with < 5000 chars (not entire document)
|
||||||
|
const isMarkdownSection = /^(#|DELETE:|Section is good)/.test(extracted)
|
||||||
|
const isShortEnough = extracted.length < 5000 // Single section should be < 5KB
|
||||||
|
|
||||||
|
if (isMarkdownSection || isShortEnough) {
|
||||||
|
return extracted
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return aiResponse
|
|
||||||
|
// Fallback: if no SECTION: tag found, treat entire response as section
|
||||||
|
// but only if it starts with markdown header or is a command
|
||||||
|
if (/^(#|DELETE:|Section is good)/.test(aiResponse)) {
|
||||||
|
return aiResponse.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If none of the above, return error indicator
|
||||||
|
return "ERROR: Response does not contain a valid section format"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -327,6 +327,46 @@ const removeFile = () => {
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- All Sessions Archive -->
|
||||||
|
<div class="sessions-archive-section">
|
||||||
|
<h2>📚 All Sessions Archive</h2>
|
||||||
|
<p class="archive-subtitle">Browse and access all previous sessions</p>
|
||||||
|
|
||||||
|
<!-- Archive Filter -->
|
||||||
|
<div class="archive-filter">
|
||||||
|
<input
|
||||||
|
v-model="searchQuery"
|
||||||
|
type="text"
|
||||||
|
placeholder="Search sessions..."
|
||||||
|
class="archive-search"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Archive Sessions -->
|
||||||
|
<div v-if="filteredSessions.length > 0" class="archive-sessions">
|
||||||
|
<div class="sessions-list">
|
||||||
|
<div
|
||||||
|
v-for="session in filteredSessions"
|
||||||
|
:key="session.sessionId"
|
||||||
|
@click="handleOpenSession(session.sessionId)"
|
||||||
|
class="archive-session-item"
|
||||||
|
:class="'status-' + session.status"
|
||||||
|
>
|
||||||
|
<div class="archive-item-header">
|
||||||
|
<span class="session-number">#{{ session.sessionId }}</span>
|
||||||
|
<span class="session-status-badge">{{ session.status }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="archive-item-prompt">{{ session.prompt.substring(0, 120) }}...</p>
|
||||||
|
<p class="archive-item-date">{{ new Date(session.createdAt).toLocaleDateString() }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="no-sessions">
|
||||||
|
<p>No sessions found</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<p>Typical session: 2-5 rounds for complete consensus. Output format: Markdown</p>
|
<p>Typical session: 2-5 rounds for complete consensus. Output format: Markdown</p>
|
||||||
</footer>
|
</footer>
|
||||||
@ -968,6 +1008,142 @@ const removeFile = () => {
|
|||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Sessions Archive Section */
|
||||||
|
.sessions-archive-section {
|
||||||
|
margin: 3rem 0 2rem 0;
|
||||||
|
padding: 2rem;
|
||||||
|
background: rgba(102, 126, 234, 0.05);
|
||||||
|
border: 1px solid rgba(102, 126, 234, 0.2);
|
||||||
|
border-radius: 16px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sessions-archive-section h2 {
|
||||||
|
margin: 0 0 0.5rem 0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: rgba(255, 255, 255, 0.95);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-subtitle {
|
||||||
|
margin: 0 0 1.5rem 0;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-filter {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-search {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background: rgba(255, 255, 255, 0.07);
|
||||||
|
border: 1px solid rgba(102, 126, 234, 0.3);
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-search::placeholder {
|
||||||
|
color: rgba(255, 255, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-search:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: rgba(102, 126, 234, 0.6);
|
||||||
|
background: rgba(102, 126, 234, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sessions-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
max-height: 500px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-session-item {
|
||||||
|
padding: 1rem;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 1px solid rgba(102, 126, 234, 0.2);
|
||||||
|
border-radius: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-session-item:hover {
|
||||||
|
background: rgba(102, 126, 234, 0.15);
|
||||||
|
border-color: rgba(102, 126, 234, 0.4);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-session-item.status-completed {
|
||||||
|
border-color: rgba(76, 175, 80, 0.3);
|
||||||
|
background: rgba(76, 175, 80, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-session-item.status-completed:hover {
|
||||||
|
background: rgba(76, 175, 80, 0.12);
|
||||||
|
border-color: rgba(76, 175, 80, 0.5);
|
||||||
|
box-shadow: 0 8px 20px rgba(76, 175, 80, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-item-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-number {
|
||||||
|
color: rgba(102, 126, 234, 0.9);
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-status-badge {
|
||||||
|
padding: 0.3rem 0.7rem;
|
||||||
|
background: rgba(102, 126, 234, 0.2);
|
||||||
|
color: rgba(102, 126, 234, 0.9);
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-session-item.status-completed .session-status-badge {
|
||||||
|
background: rgba(76, 175, 80, 0.2);
|
||||||
|
color: rgba(76, 175, 80, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-item-prompt {
|
||||||
|
margin: 0;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.archive-item-date {
|
||||||
|
margin: 0;
|
||||||
|
color: rgba(255, 255, 255, 0.4);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-sessions {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem 1rem;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.collaborative-input {
|
.collaborative-input {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|||||||
@ -52,6 +52,11 @@ onMounted(async () => {
|
|||||||
ws.connect()
|
ws.connect()
|
||||||
|
|
||||||
const messageInterval = setInterval(() => {
|
const messageInterval = setInterval(() => {
|
||||||
|
// Stop processing messages if session is stopped
|
||||||
|
if (isStopping.value) {
|
||||||
|
clearInterval(messageInterval)
|
||||||
|
return
|
||||||
|
}
|
||||||
if (ws.messages.value.length > 0) {
|
if (ws.messages.value.length > 0) {
|
||||||
const message = ws.messages.value.shift()
|
const message = ws.messages.value.shift()
|
||||||
handleWebSocketMessage(message)
|
handleWebSocketMessage(message)
|
||||||
@ -159,6 +164,9 @@ const stopSession = async () => {
|
|||||||
isStopping.value = true
|
isStopping.value = true
|
||||||
isAutoRunning.value = false
|
isAutoRunning.value = false
|
||||||
if (autoRunTimeout.value) clearTimeout(autoRunTimeout.value)
|
if (autoRunTimeout.value) clearTimeout(autoRunTimeout.value)
|
||||||
|
// Close WebSocket connection immediately
|
||||||
|
ws.disconnect()
|
||||||
|
// Then complete the session on backend
|
||||||
await completeSession()
|
await completeSession()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user