Skip to content

LOU-O: debug & observability console - #20

Merged
LinuxDevil merged 2 commits into
mainfrom
loushy/lou-o
Sep 29, 2026
Merged

LinuxDevil merged 2 commits into
mainfrom
loushy/lou-o

Conversation

@LinuxDevil

Copy link
Copy Markdown
Owner

Summary

  • O1 Logs: server/runRegistry.ts derives structured LogEntry rows from the existing ExecutionEvent stream 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-rolled VirtualList.tsx for windowed rendering, with level/phase/text filters.
  • O2 Trace: runRegistry.ts#makeTraceExporter() implements the SDK's real TraceExporter interface (onSpanStart/onSpanEnd, src/execution/tracing.ts) and is wired via ExecuteOptions.exporter. Client-side: spanReducer.ts + TracePanel.tsx (waterfall), clicking a span highlights the matching canvas node (AppState.highlightedNodeId) and jumps to matching logs.
  • O3 Step-through debugger: server/debugController.ts's DebugSession hooks the SDK's real, awaited onLLMRequest/onLLMResponse/onToolCall/onToolResult ExecuteOptions callbacks 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.
  • O4 Output: full ExecutionResult is kept on the run entry for stopped/paused states and rendered via a hand-rolled JsonTree.tsx.

Verification performed on this branch (not just trusted from the original report)

  • Read DebugSession fully: the resolver-before-notify ordering that fixes the "synchronous continue() lost" race is real (resumeResolve is assigned inside the Promise executor before onChange() fires). Confirmed onLLMRequest/onLLMResponse/onToolCall/onToolResult are real, awaited ExecuteOptions hooks in src/execution/AgentExecutor.ts (SDK root, unchanged by this PR) and that the debug session is registered before AgentExecutor.execute() is invoked, so there's no window where the very first breakpoint hook could fire before a session is listening.
  • Confirmed makeTraceExporter() matches the real TraceExporter shape in src/execution/tracing.ts.
  • Found a real operational gap: if the only WS client watching a paused run disconnects, nothing previously timed the pause out - the REST continue()/step() routes still worked, but an abandoned pause could otherwise block that run (and its checkpointed conversation) forever. Fixed: DebugSession now takes an optional pauseTimeoutMs (default 15 minutes, 0 disables it) that auto-continues an abandoned pause and flags the transition (autoResumed: true) so runRegistry.ts logs a clear warning explaining why the run kept going. Covered by two new debugController.test.ts cases.
  • Confirmed footprint is apps/agent-forge/** only - no changes to SDK root (src/**).
  • Spot-checked VirtualList.tsx (fixed-row-height windowing, only slices the currently-scrolled range) and JsonTree.tsx (collapsible, depth-limited default expansion) - nothing pathological.

Test plan

  • Root npm run build - clean
  • Root npm run typecheck - clean
  • Root npm run lint - 0 errors (381 pre-existing warnings, untouched files)
  • Root npm run test:coverage - 748 passed / 3 skipped / 5 failed (all 5 failures are pre-existing, in packages/create-loushy-agent's CLI e2e tests, unrelated to and untouched by this PR)
  • apps/agent-forge client tests (npx vitest run) - 69/69 passed
  • apps/agent-forge server tests (npm run test:server) - 34/34 passed (32 original + 2 new for the pause-timeout safety net)
  • apps/agent-forge app/server typecheck/lint - clean

🤖 Generated with Claude Code

LinuxDevil and others added 2 commits September 29, 2026 14:47
…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>
@LinuxDevil
LinuxDevil merged commit f69ad63 into main Sep 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant