How to: Integrate Debug Adapters

Goal: Give your agent debugging capabilities -- set breakpoints, step through code, inspect variables -- via the Debug Adapter Protocol (DAP).


Quick start

Add synwire-dap to your workspace dependencies and register the DapPlugin on the agent builder.

[dependencies]
synwire-dap = { version = "0.1" }
use synwire_dap::plugin::DapPlugin;
use synwire_dap::config::DapPluginConfig;

let dap = DapPlugin::new(DapPluginConfig::default());

let agent = Agent::new("debugger", "debugging assistant")
    .plugin(Box::new(dap))
    .build()?;

The plugin registers these tools: debug.launch, debug.attach, debug.set_breakpoints, debug.continue, debug.step_over, debug.step_in, debug.step_out, debug.variables, debug.evaluate, debug.stack_trace, and debug.disconnect.


Launch vs attach

DAP supports two modes for starting a debug session.

Launch mode

The plugin spawns the debug adapter and the target program together.

// The model calls:
//   debug.launch {
//     adapter: "dlv-dap",
//     program: "./cmd/myapp",
//     args: ["--config", "dev.yaml"],
//     cwd: "/home/user/project"
//   }

Attach mode

The plugin connects to an already-running process or a debug adapter listening on a port.

// The model calls:
//   debug.attach {
//     adapter: "dlv-dap",
//     pid: 12345
//   }
//
// Or attach by address:
//   debug.attach {
//     adapter: "dlv-dap",
//     host: "127.0.0.1",
//     port: 2345
//   }

Use launch mode for test debugging. Use attach mode for inspecting running services.


Example: debug a Go test

A typical debugging session with dlv-dap:

use synwire_dap::plugin::DapPlugin;
use synwire_dap::config::{DapPluginConfig, DapAdapterConfig};
use synwire_agent::Agent;

let config = DapPluginConfig {
    adapters: vec![
        DapAdapterConfig {
            name: "dlv-dap".to_string(),
            command: "dlv".to_string(),
            args: vec!["dap".to_string()],
            languages: vec!["go".to_string()],
        },
    ],
    ..Default::default()
};

let dap = DapPlugin::new(config);
let agent = Agent::new("go-debugger", "Go debugging assistant")
    .plugin(Box::new(dap))
    .build()?;

// The model can now orchestrate a debugging session:
//
//   1. debug.launch { adapter: "dlv-dap", mode: "test", program: "./pkg/auth" }
//   2. debug.set_breakpoints { path: "pkg/auth/token_test.go", line: 42 }
//   3. debug.continue {}
//   4. debug.variables { scope: "local" }
//   5. debug.stack_trace {}
//   6. debug.step_over {}
//   7. debug.variables { scope: "local" }
//   8. debug.disconnect {}

The model receives structured data for each response: variable names, types, values, and stack frame locations. It can reason about program state and suggest fixes.


Event handling

When the debuggee hits a breakpoint or throws an exception, the plugin emits a dap_stopped signal. Configure automatic inspection so the model receives context without an extra round-trip:

use synwire_dap::config::DapPluginConfig;

let config = DapPluginConfig {
    on_stopped: synwire_dap::config::StoppedBehaviour::AutoInspect {
        // Automatically fetch locals and the top 5 stack frames on every stop.
        include_locals: true,
        stack_depth: 5,
    },
    ..Default::default()
};

Available StoppedBehaviour variants:

VariantEffect
NotifyEmit the signal only; the model decides what to inspect
AutoInspect { .. }Fetch variables and stack trace automatically, inject into context
IgnoreSuppress the signal entirely (useful when scripting bulk stepping)

Security: debug.evaluate requires approval

debug.evaluate executes arbitrary expressions in the debuggee's runtime. This is marked as RiskLevel::Critical and requires explicit approval through the configured approval gate.

use synwire_agent::vfs::threshold_gate::ThresholdGate;
use synwire_core::vfs::approval::{RiskLevel, AutoDenyCallback};

// Approve up to High risk automatically; debug.evaluate (Critical) still prompts.
let gate = ThresholdGate::new(RiskLevel::High, CliPrompt);

Other DAP tools are classified as follows:

Risk levelTools
Nonedebug.variables, debug.stack_trace
Lowdebug.set_breakpoints, debug.continue, debug.step_over, debug.step_in, debug.step_out
Mediumdebug.launch, debug.attach, debug.disconnect
Criticaldebug.evaluate

Configuration

DapAdapterConfig fields:

FieldTypeDescription
nameStringIdentifier used in tool calls
commandStringAdapter binary name or path
argsVec<String>CLI arguments for the adapter process
languagesVec<String>File extensions this adapter handles
envVec<(String, String)>Extra environment variables
launch_timeoutDurationMax time to wait for adapter initialisation (default: 10s)

DapPluginConfig fields:

FieldTypeDescription
adaptersVec<DapAdapterConfig>Registered debug adapters
on_stoppedStoppedBehaviourHow to handle breakpoint/exception stops
max_concurrent_sessionsusizeLimit simultaneous debug sessions (default: 1)

See also