Repository navigation
LOU-O: debug & observability console - #20
Merged
Merged
Conversation
…ger, output inspector)
Adds real data flow (not mockup placeholders) for the BottomDrawer's
Logs/Trace/Output tabs and wires up the Topbar's Debug button:
- O1: server derives structured LogEntry rows from the existing
ExecutionEvent stream (runRegistry.ts's toLogEntries()) and forwards
them as {type:'log'} over the existing WS channel. Client keeps a
capped ring buffer (state/logReducer.ts) rendered through a small
hand-rolled windowed list (components/debug/VirtualList.tsx) with a
level/phase/text filter bar (LogsPanel.tsx).
- O2: server hooks the SDK's real TraceExporter (onSpanStart/onSpanEnd)
per run and forwards span events as {type:'span'}. Client renders a
waterfall from real timestamps (TracePanel.tsx); clicking a span jumps
to matching log lines and highlights the corresponding canvas node.
- O3: step-through debugger built on AgentExecutor's real
onLLMRequest/onLLMResponse/onToolCall/onToolResult hooks
(server/debugController.ts's DebugSession) - breakpoints pause a live
await inside those hooks at llm/tool node boundaries (before/after).
Step arms a one-shot pause at the next boundary; Continue resumes.
Inspector exposes breakpoint toggles on llm/tool nodes; DebugBar shows
live message array + step count while paused.
- O4: OutputPanel renders the final/paused ExecutionResult (now kept in
full on the run entry, not just resultText) as a collapsible JSON tree
(JsonTree.tsx), no external dependency.
All new wire types (log/span/debug) ride the existing WS connection from
LOU-N - no second channel. New tests: server/debugController.test.ts,
extended runRegistry.test.ts/app.test.ts, and
src/state/__tests__/{logReducer,spanReducer}.test.ts.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
DebugSession.maybePause() could block a run paused at a breakpoint forever with no way to resume if the only WS client watching it disconnected - the REST continue()/step() routes still worked, but nothing would auto-recover an abandoned local dev session. Add a 15-minute default pauseTimeoutMs (0 disables it) that auto-continues the run and flags the transition as autoResumed so runRegistry.ts can log a clear warning explaining why the run kept going. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
server/runRegistry.tsderives structuredLogEntryrows from the existingExecutionEventstream and forwards them as{type:'log'}over the existing LOU-N WS channel (no second WS server). Client-side:src/state/logReducer.ts(2000-entry ring buffer),LogsPanel.tsx+ a hand-rolledVirtualList.tsxfor windowed rendering, with level/phase/text filters.runRegistry.ts#makeTraceExporter()implements the SDK's realTraceExporterinterface (onSpanStart/onSpanEnd,src/execution/tracing.ts) and is wired viaExecuteOptions.exporter. Client-side:spanReducer.ts+TracePanel.tsx(waterfall), clicking a span highlights the matching canvas node (AppState.highlightedNodeId) and jumps to matching logs.server/debugController.ts'sDebugSessionhooks the SDK's real, awaitedonLLMRequest/onLLMResponse/onToolCall/onToolResultExecuteOptionscallbacks and blocks on a pending promise when a breakpoint (llm:before|after,tool:<name>:before|after) matches - a genuine pause of live execution, not a simulated one. New routes:GET/PUT /agents/:id/debug,POST /agents/:id/debug/{continue,step}. Client-side: breakpoint toggle in Inspector,DebugBar.tsx.ExecutionResultis kept on the run entry forstopped/pausedstates and rendered via a hand-rolledJsonTree.tsx.Verification performed on this branch (not just trusted from the original report)
DebugSessionfully: the resolver-before-notify ordering that fixes the "synchronous continue() lost" race is real (resumeResolveis assigned inside thePromiseexecutor beforeonChange()fires). ConfirmedonLLMRequest/onLLMResponse/onToolCall/onToolResultare real, awaitedExecuteOptionshooks insrc/execution/AgentExecutor.ts(SDK root, unchanged by this PR) and that the debug session is registered beforeAgentExecutor.execute()is invoked, so there's no window where the very first breakpoint hook could fire before a session is listening.makeTraceExporter()matches the realTraceExportershape insrc/execution/tracing.ts.continue()/step()routes still worked, but an abandoned pause could otherwise block that run (and its checkpointed conversation) forever. Fixed:DebugSessionnow takes an optionalpauseTimeoutMs(default 15 minutes,0disables it) that auto-continues an abandoned pause and flags the transition (autoResumed: true) sorunRegistry.tslogs a clear warning explaining why the run kept going. Covered by two newdebugController.test.tscases.apps/agent-forge/**only - no changes to SDK root (src/**).VirtualList.tsx(fixed-row-height windowing, only slices the currently-scrolled range) andJsonTree.tsx(collapsible, depth-limited default expansion) - nothing pathological.Test plan
npm run build- cleannpm run typecheck- cleannpm run lint- 0 errors (381 pre-existing warnings, untouched files)npm run test:coverage- 748 passed / 3 skipped / 5 failed (all 5 failures are pre-existing, inpackages/create-loushy-agent's CLI e2e tests, unrelated to and untouched by this PR)apps/agent-forgeclient tests (npx vitest run) - 69/69 passedapps/agent-forgeserver tests (npm run test:server) - 34/34 passed (32 original + 2 new for the pause-timeout safety net)apps/agent-forgeapp/servertypecheck/lint- clean🤖 Generated with Claude Code