feat: Paseo-like orchestrator Phase 1-2 — trace system, session persistence, timeline, run_command, auto-fix loop
Phase 1: Trace System + Observability - tool_traces DB table + insert/update service - tool_trace_start/tool_trace_finish WS frames (contracts + FE types) - Instrumented tool-phase.ts with timing around every tool call - GET /api/chats/:id/traces paginated endpoint - Trace viewer frontend (collapsible panel with timing bars + token breakdown) Phase 2: Session Persistence + Resume - agent_snapshots table (UPSERT per chat, persisted on turn boundaries) - save/load/delete service functions - Agent snapshot sent on WS reconnect - Session timeline view (vertical timeline with scroll-to + restore) Tooling: - run_command tool (execFile, 30s timeout, 32KB cap, path-guarded) - Auto-fix loop: after write tools, runs pnpm build, injects errors into next turn
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Pencil, Send, X } from 'lucide-react';
|
||||
import { History, Pencil, Send, X } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { api } from '@/api/client';
|
||||
import { useSessionStream } from '@/hooks/useSessionStream';
|
||||
import { MessageList } from '@/components/MessageList';
|
||||
import { ChatInput } from '@/components/ChatInput';
|
||||
import { StaleStreamBanner } from '@/components/StaleStreamBanner';
|
||||
import { SessionTimeline } from '@/components/SessionTimeline';
|
||||
import { TraceViewer } from '@/components/TraceViewer';
|
||||
import { sendToChat } from '@/lib/events';
|
||||
|
||||
interface Props {
|
||||
@@ -25,6 +27,7 @@ interface Props {
|
||||
export function ChatPane({ sessionId, chatId, projectId, agentId, onAgentChange, sessionChats, webSearchEnabled }: Props) {
|
||||
const stream = useSessionStream(sessionId);
|
||||
const lastErrorRef = useRef<string | null>(null);
|
||||
const [showTimeline, setShowTimeline] = useState(false);
|
||||
const [queue, setQueue] = useState<{ id: string; text: string }[]>([]);
|
||||
const queueIdRef = useRef(0);
|
||||
const processingRef = useRef(false);
|
||||
@@ -203,11 +206,41 @@ export function ChatPane({ sessionId, chatId, projectId, agentId, onAgentChange,
|
||||
}
|
||||
}
|
||||
|
||||
const handleScrollToMessage = useCallback((messageId: string) => {
|
||||
const el = document.getElementById(`msg-${messageId}`);
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full min-h-0">
|
||||
<div className="flex flex-col h-full min-h-0 relative">
|
||||
{chatMessages.length > 0 && (
|
||||
<div className="absolute top-2 right-2 z-10">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowTimeline((v) => !v)}
|
||||
className={`
|
||||
inline-flex items-center gap-1 px-2 py-1 rounded-md text-xs font-medium
|
||||
transition-colors border
|
||||
${showTimeline
|
||||
? 'bg-primary text-primary-foreground border-primary'
|
||||
: 'bg-background text-muted-foreground border-border hover:bg-muted hover:text-foreground'
|
||||
}
|
||||
`}
|
||||
aria-label={showTimeline ? 'Close timeline' : 'Open timeline'}
|
||||
>
|
||||
<History size={12} />
|
||||
Timeline
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* v1.11.5: ContextBar moved into ChatInput (above the agent picker). */}
|
||||
<MessageList messages={chatMessages} sessionChats={sessionChats} />
|
||||
|
||||
<TraceViewer chatId={chatId} />
|
||||
|
||||
{/* Queued messages */}
|
||||
{queue.length > 0 && (
|
||||
<div className="border-t">
|
||||
@@ -275,6 +308,16 @@ export function ChatPane({ sessionId, chatId, projectId, agentId, onAgentChange,
|
||||
messages={chatMessages}
|
||||
modelContextLimit={modelContextLimit}
|
||||
/>
|
||||
|
||||
{/* Timeline overlay panel */}
|
||||
{showTimeline && (
|
||||
<SessionTimeline
|
||||
messages={chatMessages}
|
||||
chatId={chatId}
|
||||
onClose={() => setShowTimeline(false)}
|
||||
onScrollToMessage={handleScrollToMessage}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user