debug
Drive one DAP debug session; adjacent debug UI code reuses the same subsystem for logs, raw SSE capture, reports, profiling, and system diagnostics.
Source
- Entry:
packages/coding-agent/src/tools/debug.ts - Model-facing prompt:
packages/coding-agent/src/prompts/tools/debug.md - Key collaborators:
packages/coding-agent/src/dap/session.ts— session lifecycle, breakpoint/state cachepackages/coding-agent/src/dap/client.ts— adapter process/socket transport, DAP message looppackages/coding-agent/src/dap/config.ts— adapter resolution and auto-selectionpackages/coding-agent/src/dap/defaults.json— built-in adapter definitionspackages/coding-agent/src/dap/types.ts— request/response/capability shapespackages/coding-agent/src/tools/tool-timeouts.ts— per-tool timeout clamppackages/coding-agent/src/debug/index.ts— interactive debug selector menupackages/coding-agent/src/debug/log-viewer.ts— recent-log TUI viewerpackages/coding-agent/src/debug/raw-sse.ts— raw SSE TUI viewerpackages/coding-agent/src/debug/raw-sse-buffer.ts— bounded SSE capture bufferpackages/coding-agent/src/debug/profiler.ts— CPU/heap profiling helperspackages/coding-agent/src/debug/report-bundle.ts—.tar.gzreport bundling, log source, cache cleanuppackages/coding-agent/src/debug/system-info.ts— system snapshot collection and env redaction
Inputs
| Field | Type | Required | Description |
|---|---|---|---|
action |
"launch" | "attach" | "set_breakpoint" | "remove_breakpoint" | "set_instruction_breakpoint" | "remove_instruction_breakpoint" | "data_breakpoint_info" | "set_data_breakpoint" | "remove_data_breakpoint" | "continue" | "step_over" | "step_in" | "step_out" | "pause" | "evaluate" | "stack_trace" | "threads" | "scopes" | "variables" | "disassemble" | "read_memory" | "write_memory" | "modules" | "loaded_sources" | "custom_request" | "output" | "terminate" | "sessions" |
Yes | Dispatch key for the tool switch in packages/coding-agent/src/tools/debug.ts. |
program |
string |
No | Launch target path. Required for launch. Resolved relative to cwd if provided, otherwise session cwd. |
args |
string[] |
No | Program argv for launch. |
adapter |
string |
No | Explicit adapter name. Otherwise selectLaunchAdapter() / selectAttachAdapter() auto-pick from packages/coding-agent/src/dap/config.ts. |
cwd |
string |
No | Launch/attach working directory. Defaults to session cwd. |
file |
string |
No | Source file path for source breakpoints. |
line |
number |
No | Source line for source breakpoints. |
function |
string |
No | Function breakpoint name. Mutually exclusive with file+line in breakpoint actions. |
name |
string |
No | Data breakpoint info target name. Required for data_breakpoint_info. |
condition |
string |
No | Conditional expression for source/function/instruction/data breakpoints. |
hit_condition |
string |
No | Hit-count condition for instruction/data breakpoints. |
expression |
string |
No | Expression or raw debugger command. Required for evaluate. |
context |
string |
No | Evaluate context. Defaults to "repl". Passed through as DAP evaluate context. |
frame_id |
number |
No | Frame selector for evaluate, scopes, data_breakpoint_info. scopes and evaluate default to the current stopped frame when omitted. |
scope_id |
number |
No | Variables reference from a scope. Accepted by variables; also used as a fallback variables reference for data_breakpoint_info. |
variable_ref |
number |
No | Variables reference for variables; preferred over scope_id when both are present. |
pid |
number |
No | Local process id for attach. attach requires pid or port. |
port |
number |
No | Remote attach port. If no adapter is forced, attach prefers debugpy when port is present. |
host |
string |
No | Remote attach host for attach. |
levels |
number |
No | Max stack frames for stack_trace. |
memory_reference |
string |
No | Memory reference/address for disassemble, read_memory, write_memory. disassemble uses this when provided; otherwise it falls back to the current stopped location's instruction-pointer reference if the adapter supplied one. |
instruction_reference |
string |
No | Instruction breakpoint reference; required for instruction breakpoint actions. Not used by disassemble. |
instruction_count |
number |
No | Required for disassemble. |
instruction_offset |
number |
No | Instruction offset for disassemble. |
count |
number |
No | Byte count for read_memory. Required there. |
data |
string |
No | Base64 payload for write_memory. Required there. |
data_id |
string |
No | Data breakpoint id. Required for set_data_breakpoint / remove_data_breakpoint. |
access_type |
"read" | "write" | "readWrite" |
No | Access filter for set_data_breakpoint. |
command |
string |
No | Custom DAP request command. Required for custom_request. |
arguments |
Record<string, unknown> |
No | Custom DAP request body for custom_request. |
offset |
number |
No | Offset for instruction breakpoints, disassembly, memory read, memory write. |
resolve_symbols |
boolean |
No | disassemble symbol-resolution flag. |
allow_partial |
boolean |
No | write_memory partial-write allowance. |
start_module |
number |
No | Modules pagination start index for modules. |
module_count |
number |
No | Modules pagination count for modules. |
timeout |
number |
No | Per-request timeout in seconds. Default 30, clamped to 5..300. |
Action-specific requirements
launch:programattach:pidorportset_breakpoint/remove_breakpoint:function, orfile+lineset_instruction_breakpoint/remove_instruction_breakpoint:instruction_referencedata_breakpoint_info:nameset_data_breakpoint/remove_data_breakpoint:data_idevaluate:expressionvariables:variable_reforscope_iddisassemble: capabilitysupportsDisassembleRequest, plusinstruction_count, and eithermemory_referenceor a current stopped location withinstructionPointerReferenceread_memory: capabilitysupportsReadMemoryRequest, plusmemory_referenceandcountwrite_memory: capabilitysupportsWriteMemoryRequest, plusmemory_referenceanddatamodules: capabilitysupportsModulesRequestloaded_sources: capabilitysupportsLoadedSourcesRequestcustom_request:command
Interactive selector values
packages/coding-agent/src/debug/index.ts also exposes a fixed UI-only selector with values open-artifacts, performance, work, dump, memory, logs, system, raw-sse, transcript, clear-cache. These are not model-callable through debugSchema; they are local TUI menu routes.
Outputs
The agent tool returns a standard toolResult() payload from packages/coding-agent/src/tools/debug.ts:
content: one text block. Every action renders human-readable text; there is no structured JSON block incontent.details.action: echoed action.details.success: always initializedtrue; failures surface by throwing before a result is returned.details.snapshot: present for actions that operate on or create a session, usingDapSessionSummaryfrompackages/coding-agent/src/dap/types.ts.- Action-specific
detailsfields:launch/attach:adapter- breakpoint actions:
breakpoints,functionBreakpoints,instructionBreakpoints,dataBreakpoints data_breakpoint_info:dataBreakpointInfocontinue/step_*:state,timedOutthreads:threadsstack_trace:stackFramesscopes:scopesvariables:variablesevaluate:evaluationdisassemble:disassemblyread_memory:memoryAddress,memoryData,unreadableByteswrite_memory:bytesWrittenmodules:modulesloaded_sources:sourcescustom_request:customBodyoutput:outputsessions:sessions
Streaming/UI behavior:
- The tool renderer merges call and result (
mergeCallAndResult: true) and renders inline. debug.tsitself does not emit progress updates through_onUpdate; result delivery is single-shot.- Approval is action-sensitive: read-only actions (
output,threads,stack_trace,scopes,variables,disassemble,read_memory,loaded_sources,modules,sessions) request read approval; all other actions request exec approval. - The interactive selector is UI-driven instead of model-driven. It swaps TUI components, appends status lines to the chat pane, opens files in external viewers, or writes archives/temp files.
Side-channel artifacts outside the model tool result:
createReportBundle()writesomp-report-<timestamp>.tar.gzunder the reports dir and returns the filesystem path to the UI handler.#handleWorkReport()writes/tmp/work-profile-<Date.now()>.svgbefore opening it.RawSseViewerComponentandDebugLogViewerComponentcan copy captured text to the clipboard.
Flow
- Tool registration is conditional:
DebugTool.createIf()inpackages/coding-agent/src/tools/debug.tsreturnsnullunlesssession.settings.get("debug.enabled")is true.packages/coding-agent/src/tools/index.tswires the factory and rechecks the same setting in tool filtering. DebugTool.execute()clampsparams.timeoutthroughclampTimeout("debug", params.timeout)and composes the callerAbortSignalwithAbortSignal.timeout(...).launchandattachresolve cwd/program paths, select an adapter inpackages/coding-agent/src/dap/config.ts, then delegate todapSessionManager.launch()/.attach().DapSessionManager.launch()/.attach()enforce the single-session rule with#ensureLaunchSlot(), spawn the adapter throughDapClient.spawn(), register listeners, sendinitialize, cache capabilities, start listening for an initial stop event before sendinglaunch/attach, then complete theinitialized→configurationDonehandshake in#completeConfigurationHandshake().DapClient.spawn()starts the adapter detached withNON_INTERACTIVE_ENV. Most adapters use stdio; socket-mode adapters (dlv) use#spawnSocketUnix()on Linux or#spawnSocketClientAddr()on macOS/other.#registerSession()inpackages/coding-agent/src/dap/session.tsinstalls reverse-request handlers:runInTerminal: spawns the requested debuggee command detached viaptree.spawn()and returns{ processId }startDebugging: logs the child-session request and returns{}; it does not create nested sessions- events:
output,initialized,stopped,continued,exited,terminatedupdate cached session state
- Operational actions (
set_breakpoint,evaluate,threads,read_memory,custom_request, and similar) calldapSessionManagermethods. Most flow through#sendRequestWithConfig(), which first sendsconfigurationDonewhen required, then sends the DAP request, then updateslastUsedAt. - Breakpoint actions maintain local cached breakpoint sets in
DapSessionManagerand remap adapter responses back onto those cached records. continueand the three step actions clear cached stop state, subscribe forstopped/terminated/exitedbefore sending the DAP request, then#awaitStopOutcome()either returns the new stopped location or reports that the program is still running after timeout.pausesends DAPpause, waits for a stopped event if needed, and reuses cached stop state if the program was already stopped.stack_trace,scopes,variables, andevaluatedefault to the current stopped thread/frame when the caller omits ids and cached state is available.outputreads the in-memory output ring fromDapSessionManager.getOutput().terminatesendsterminatewhen supported, always attemptsdisconnect, marks the session terminated, and disposes the client.sessionsreads the manager’s current map and formats all summaries. Although the manager stores a map, only one active session can exist because new launch/attach calls are blocked until the active one is terminated or cleaned up.- The interactive selector in
packages/coding-agent/src/debug/index.tsbuilds aSelectListof fixed values and dispatches each to a handler:
performance:startCpuProfile(), wait for Enter/Escape, stop profiling, read a 30-second work profile withgetWorkProfile(30), then bundle viacreateReportBundle()work: readgetWorkProfile(30), write a temp SVG, open it externallydump: create a report bundle immediatelymemory: force GC, callBun.generateHeapSnapshot("v8"), then bundlelogs: build aDebugLogSourceand mountDebugLogViewerComponentraw-sse: resolve aRawSseDebugBufferfrom the session and mountRawSseViewerComponentsystem: callcollectSystemInfo()and renderformatSystemInfo()into the chat paneopen-artifacts: open the current session artifact directory if it existstranscript: delegates toctx.handleDebugTranscriptCommand()clear-cache: show confirmation, then remove artifact directories older than 30 days withclearArtifactCache()
Modes / Variants
- Availability gate
- Tool hidden when
debug.enabledis false.
- Tool hidden when
- Adapter selection
launch: explicitadapterwins; otherwiseselectLaunchAdapter()ranks available adapters by extension match, root-marker match, then native-debugger preference (gdb,lldb-dap) for extensionless binaries.attach: explicitadapterwins; otherwise remoteportprefersdebugpy, then native debuggers, then first available adapter.
- Transport
- stdio adapters: direct
stdin/stdoutframing. - socket adapters: Unix domain socket on Linux; TCP callback on macOS/other.
- stdio adapters: direct
- DAP agent-tool actions
launch— spawn adapter, initialize session, maybe stop on entry; returns formatted session snapshot anddetails.adapter.attach— connect to a live process or remote port; same output shape aslaunch.set_breakpoint— source or function breakpoint add/update; returns the current breakpoint list for that target.remove_breakpoint— source or function breakpoint removal; returns the remaining breakpoint list.set_instruction_breakpoint/remove_instruction_breakpoint— requiresupportsInstructionBreakpoints; return current instruction breakpoint list.data_breakpoint_info— requiresupportsDataBreakpoints; asks the adapter for adataId, access types, and description forname.set_data_breakpoint/remove_data_breakpoint— requiresupportsDataBreakpoints; return the cached data-breakpoint list.continue/step_over/step_in/step_out— return text describing whether execution stopped, terminated, or kept running, plusdetails.stateanddetails.timedOut.pause— interrupts a running target and returns a stopped snapshot.evaluate— adapter expression evaluation; defaults context torepl.stack_trace— fetches frames for the resolved thread.threads— fetches current threads.scopes— frame scopes for an explicitframe_idor the current stopped frame.variables— variables forvariable_reforscope_id.disassemble— requiresupportsDisassembleRequest; disassembles aroundmemory_reference, or around the current stopped instruction pointer when no memory reference is supplied.read_memory— requiresupportsReadMemoryRequest; returns address, base64 data, unreadable-byte count.write_memory— requiresupportsWriteMemoryRequest; writes base64 data and reports bytes written.modules— requiresupportsModulesRequest; optional pagination viastart_module/module_count.loaded_sources— requiresupportsLoadedSourcesRequest; returns loaded source descriptors.custom_request— sends any DAP request name with arbitrary arguments.output— dumps captured stdout/stderr/console text from the session cache.terminate— disconnects and disposes the active session; returnsNo debug session to terminate.when none exists.sessions— lists all cached session summaries.
- Interactive selector routes (UI-only)
logs— loads today’s log tail and optional older daily log files intoDebugLogViewerComponent; supports copy, range selection, pid filtering, load-older.raw-sse— live view over the session’sRawSseDebugBuffer; supports tail-follow, scrolling, copy-all.performance— CPU profile + 30-second work profile + report bundle.memory— heap snapshot + report bundle.dump— report bundle without profiler artifacts.work— standalone work-profile flamegraph export/open.system— formatted OS/arch/CPU/memory/version/cwd/shell/terminal dump.open-artifacts/transcript/clear-cache— artifact directory open, transcript export, artifact-cache pruning.
Side Effects
- Filesystem
- Resolves program/file/cwd paths against the session cwd.
- Report creation writes
.tar.gzbundles and may read the session JSONL, artifact files, subagent session JSONLs, and log files. - Work-profile export writes
/tmp/work-profile-<timestamp>.svg. - Log source reads daily log files from the logs dir.
- Artifact-cache cleanup removes session artifact directories older than the cutoff.
resolveRawSseDebugBuffer()may attach a non-enumerablerawSseDebugBufferproperty to the owner object.
- Network
- Socket-mode adapters bind/connect local sockets.
- Remote attach may connect through the adapter to a remote debug port.
- Subprocesses / native bindings
- Spawns debugger adapters (
gdb,lldb-dap,python -m debugpy.adapter,dlv, and others fromdefaults.json) detached. - Reverse DAP
runInTerminalrequests spawn the debuggee detached viaptree.spawn(). getWorkProfile(30)comes from@oh-my-pi/pi-natives.- CPU profiling uses
node:inspector/promises; heap snapshots useBun.generateHeapSnapshot("v8"); raw/log viewers sanitize text via@oh-my-pi/pi-natives. openPath()launches the OS default file/browser handler for artifact dirs and SVGs.- Log/raw-SSE viewers can call
copyToClipboard().
- Spawns debugger adapters (
- Session state (transcript, memory, jobs, checkpoints, registries)
DapSessionManagerkeeps session summaries, breakpoints, threads, stack frames, stop location, output capture, capabilities, and last-used timestamps in memory.- Active-session id is global to the singleton
dapSessionManager. RawSseDebugBufferstores recent SSE events per owner/session.- The tool is
exclusive; concurrent debug tool calls are blocked by the scheduler.
- User-visible prompts / interactive UI
- Debug selector shows confirmation before cache deletion.
- Performance profiling temporarily hijacks editor Enter/Escape handlers until profiling stops.
- Log/raw-SSE viewers replace the editor pane with custom components.
- Background work / cancellation
- Every DAP request accepts an
AbortSignal; timeouts and caller cancellation abort the active request, not the whole session lifetime. DapSessionManagerruns a background cleanup loop every 30 seconds.- Raw SSE viewers subscribe to buffer updates until closed.
- Every DAP request accepts an
Limits & Caps
- Tool timeout clamp:
default=30,min=5,max=300inpackages/coding-agent/src/tools/tool-timeouts.ts. - Per-request DAP default timeout:
DEFAULT_REQUEST_TIMEOUT_MS = 30_000inpackages/coding-agent/src/dap/client.ts. - Single active session: enforced by
#ensureLaunchSlot()inpackages/coding-agent/src/dap/session.ts. - Idle session cleanup:
IDLE_TIMEOUT_MS = 10 * 60 * 1000, checked everyCLEANUP_INTERVAL_MS = 30 * 1000. - Adapter liveness heartbeat:
HEARTBEAT_INTERVAL_MS = 5 * 1000. - Output capture cap:
MAX_OUTPUT_BYTES = 128 * 1024; older text is trimmed in ~1 KiB slices andoutputTruncatedis recorded. - Initial stop capture timeout after launch/attach:
STOP_CAPTURE_TIMEOUT_MS = 5_000. - Socket-mode adapter readiness timeout:
10_000ms inwaitForCondition()and TCP connect timeout logic inpackages/coding-agent/src/dap/client.ts. - Raw SSE buffer caps in
packages/coding-agent/src/debug/raw-sse-buffer.ts:MAX_RAW_SSE_EVENTS = 1_000MAX_RAW_SSE_CHARS = 512_000MAX_RAW_SSE_EVENT_CHARS = 64_000per event, with: omp-debug-truncated ...marker appended on trim
- Log viewer window in
packages/coding-agent/src/debug/log-viewer.ts:INITIAL_LOG_CHUNK = 50LOAD_OLDER_CHUNK = 50
- Report/log ingestion caps in
packages/coding-agent/src/debug/report-bundle.ts:MAX_LOG_LINES = 5000for interactive log readingMAX_LOG_BYTES = 2 * 1024 * 1024tail-read ceiling- report bundles include only the last
1000log lines - subagent session inclusion is capped at the most recent
10JSONL files
- Interactive profiling windows in
packages/coding-agent/src/debug/index.ts: both performance and work reports requestgetWorkProfile(30). - Artifact cache pruning default:
30days inclearArtifactCache()and the selector confirmation text.
Errors
- Parameter validation in
packages/coding-agent/src/tools/debug.tsthrowsToolErrorwith explicit messages such as:program is required for launchattach requires pid or portset_breakpoint requires file+line or functionvariables requires variable_ref or scope_idinstruction_count is required for disassembledisassemble requires memory_reference unless the current stop location has an instruction pointer referencememory_reference is required for read_memorycount is required for read_memorydata is required for write_memorycommand is required for custom_request
- Adapter selection failure throws
No debugger adapter available. Installed adapters: .... - Capability-gated actions throw from
requireCapability(...), e.g.Active adapter does not support memory reads. - No-session and state errors come from
DapSessionManager, e.g.No active debug session. Launch or attach first.,No active stack frame. Run stack_trace first or supply frame_id.,Debugger reported no threads. - Launching a second live session throws
Debug session <id> is still active. Terminate it before launching another. - DAP transport/request failures surface as thrown errors from
DapClient:DAP request <command> timed out after <ms>msDAP event <event> timed out after <ms>msDAP adapter <name> is not runningDAP adapter exited (code N): <stderr>orDAP adapter exited unexpectedly (code N)- adapter response
messagewhen a DAP request fails
continue/step_*are intentionally non-fatal when the target stays running past the timeout: they returndetails.timedOut = trueandstate: "running"instead of throwing.terminatesuppresses adapter errors while sendingterminate/disconnect; it still disposes the client and returns the last summary when possible.- Interactive selector handlers report UI errors instead of throwing:
- profiler start/stop, report bundling, log reading, system-info collection, cache clearing, and artifact opening use
ctx.showError(...)/ctx.showWarning(...) - empty logs and empty artifact caches are warnings/status messages, not failures
- copy failures in log/raw-SSE viewers become status/error text in the UI
- profiler start/stop, report bundling, log reading, system-info collection, cache clearing, and artifact opening use
- Report-bundle helpers are intentionally best-effort for many file reads: missing session files, missing artifact dirs, unreadable artifact files, missing log dirs, inaccessible cache dirs, and missing subagent files are skipped silently.
collectSystemInfo()is best-effort for CPU probing; failure there falls back toUnknown CPU.
Notes
packages/coding-agent/src/prompts/tools/debug.mdtells the model only one active session is supported; that is not advisory, it is enforced in code.configurationDoneis sent automatically both during launch/attach handshake and lazily before later requests if the adapter required it and the initial handshake did not complete.startDebuggingreverse requests are acknowledged but not implemented; child debug sessions are not spawned.outputexposes the mergedoutputevent stream only; the tool does not distinguish stdout, stderr, and console categories.- Session summaries expose
needsConfigurationDone; this is derived from adapter capabilities and whetherconfigurationDonehas been sent. - Source breakpoint file paths are normalized with
path.resolve()before caching and sending to the adapter. evaluatedefaults torepl, so the tool can forward raw debugger commands when the adapter supports them.disassembleresolves its target frommemory_referencefirst, then the current stopped session'sinstructionPointerReference; it throws if neither is present.RawSseDebugBuffer.recordEvent()incrementstotalEventsbefore bounded retention. A snapshot can therefore show fewer retained records than total observed events.- Raw SSE buffer listener failures are swallowed so viewer bugs do not break capture.
createDebugLogSource()walks daily log files newest-first, butloadOlderLogs()reverses each requested slice before concatenation so older chunks prepend in chronological order.clearArtifactCache()deletes directories by directory mtime, not per-file age.addDirectoryToArchive()reads artifact files as text withBun.file(...).text(). Binary artifact contents are not preserved byte-for-byte in the report bundle.- The tool renderer truncates displayed output for the TUI preview, but the underlying text result still contains the full returned string.