Debugger and Inspector¶
The optional kedi-debugger package serves both editor extensions through DAP.
It stops at Kedi statements and procedure boundaries while leaving the existing
runtime, adapters, approval policy, and concurrency in charge of execution.
Source-checkout feature
Install the matching local runtime, debugger, and editor extension. These
changes are not an automatic Marketplace, Zed registry, or PyPI release.
Older Kedi builds do not contain the required kedi.debugging hooks.
One Interpreter¶
In managed mode, the updated extensions install the debugger automatically into
the shared ~/.kedi/editor-venv, including upgrades of older managed environments.
The installer validates both the debugger package and the required runtime hooks.
This path requires matching published packages; the source-checkout warning above
still applies until release.
For a selected host interpreter or unpublished local development, install explicitly:
python -m pip install -e /path/to/kedi
python -m pip install --no-deps -e /path/to/kedi/debugger
python -c 'import kedi_debugger; from kedi.debugging import DebugEvent, observe_execution'
The shared managed environment is ~/.kedi/editor-venv. Existing VS Code and Zed
host-Python overrides also govern debugging. Host interpreters are not modified
automatically. Project dependencies must be installed in that same environment.
Start Without a Model¶
Save this as debug.kedi, put a breakpoint on the assignment inside double,
and launch it through the editor's debugger:
@double(value: int) -> int:
[result: int] = `value * 2`
= `result`
[number: int] = `21`
[answer: int] = `double(number)`
> show: The computed answer is <answer>.
Inspect value and result in Locals, navigate the Kedi stack, step out,
and continue. The example makes no model calls. The debug console displays normal
program output; it is not a Python REPL.
VS Code¶
Use Run and Debug with the Kedi launch configuration:
{
"version": "0.2.0",
"configurations": [{
"type": "kedi",
"request": "launch",
"name": "Debug Kedi",
"program": "${file}",
"cwd": "${workspaceFolder}",
"stopOnEntry": true
}]
}
The extension requires workspace trust. Select the interpreter using the existing Kedi command or settings described in VS Code.
Zed¶
Use a Kedi debugger task with adapter kedi and the same launch fields.
The updated extension delegates to python -m kedi_debugger --stdio using the
same Python selection as its language server. See Zed for host setup.
Zed reserves the task's adapter field for the debugger name, so use
kediAdapter for an optional model-framework override. For example, in
.zed/debug.json:
[
{
"label": "Debug Kedi",
"adapter": "kedi",
"request": "launch",
"program": "$ZED_FILE",
"cwd": "$ZED_WORKTREE_ROOT",
"stopOnEntry": true
}
]
Save the file and trust the project before starting. The pinned Zed extension API does not expose the VS Code-style trust or unsaved-buffer callbacks.
Launch Environment¶
Both editors load the nearest .env from the launch cwd or its parents before
the worker imports Kedi or selects a model. Without cwd, discovery starts at
the program's directory. Only the nearest file is loaded. This includes API keys,
KEDI_ADAPTER, KEDI_ADAPTER_MODEL, and MODEL_NAME, even if the editor was
started outside a terminal.
Launch env entries override the inherited process environment, which overrides
.env values. null removes a variable from both sources; "" keeps an empty
value. Dotenv quoting and ${NAME} interpolation are supported. Explicit launch
model and adapter settings override environment defaults. These rules apply to
both VS Code and Zed. Keep credentials out of committed launch configurations.
To skip dotenv discovery, set PYTHON_DOTENV_DISABLED=1 in launch env or the
inherited environment. The debugger sets this flag in the worker after loading
its environment so later automatic dotenv loads cannot restore removed keys or
load unrelated credentials. Restart the debug session after editing .env.
Known loaded credentials are masked in debug output and inspection. An invalid
or unreadable .env fails launch without exposing its contents; a missing file
is allowed.
Model Boundaries¶
A model-backed program uses the usual Kedi directives:
> model: openai:gpt-5.6-luna
[country] = Sweden
>> The capital of <country> is [capital].
> show: The capital is <capital>.
Enable the Before model dispatch and After model result event filters in
the debugger's breakpoint controls. Their IDs are model_input and model_result.
The first stop shows the prepared input; the second shows the result and available
request/usage observations. A request-transforming hook can change the assembled
request, so inspect request for the actual Kedi request rather than treating the
earlier input as a provider wire transcript.
Waiting on a capture does not stop its independent producer. Continue and step commands resume paused peer lanes by default, so a capture can receive its result. With explicit single-thread stepping, a paused producer needs its own continue command. The debugger does not serialize parallel work or resend a request after resume.
Request assembly and nested tool execution inside a model transaction are observation-only: they cannot pause while holding that transaction's resources. A propagated tool error can stop after those resources are released. Exceptions from parallel model jobs also reach the exception filter.
Inspection and Privacy¶
Source snapshots are frozen for the session. If a source changes before starting or resuming, restart the debugger to run the edited program. Known environment secrets are masked in source responses without shifting their line positions.
Snapshots are bounded and detached from live objects. Completed captures expose
their existing values and typed fields without resolving or calling the model
again. Pending promises are not forced; unknown Python objects remain opaque.
Large values do not consume the slots reserved for later sibling bindings.
If a scope exceeds its snapshot limits, a <snapshot> notice explains the partial
view. A resolved promise shows the memoized value actually read by the program,
even if the shared model-result mapping subsequently changes.
Names and captured paths such as
answer, record.name, or items[0] can be watched. Calls, assignments, operator
evaluation and dynamic property access are rejected. Resuming invalidates that
lane's inspection handles. Recent changes compare captured values only, not all
possible mutations of arbitrary Python objects.
Distinct dictionary keys such as 1 and "1" can share a displayed name. Watch
evaluation rejects those ambiguous paths instead of selecting an arbitrary value;
inspect their entries in the variables view.
Token usage counters, cache/reasoning token details and token limits remain
visible; the word token in a metric name does not make it a credential.
Access/refresh tokens, API keys and sensitive fields inside usage containers
remain redacted. Known environment secret values are masked even under otherwise
public field names.
Known environment secrets and sensitive fields are redacted, but unknown secrets inside prose cannot be guaranteed absent. No persistent transcript or Logfire exporter is configured. Treat screenshots and console output as potentially sensitive. The older executor debug exporter has a different privacy contract and is not this inspector.
Limits and Lifecycle¶
Pause is cooperative. Already-running provider calls and their deadlines continue. Embedded Python blocks are opaque; blocking Python may require Terminate. The supervisor remains responsive and owns only its worker/process descendants. Disconnect terminates the launched worker, not unrelated Python processes.
Use Restart/Rerun after saving source or .env changes. The editor ends the
old adapter session and launches a fresh one; the DAP restart shutdown flag is
accepted. This is a fresh execution, not hot reload. Source edits detected while
paused produce an explanatory console message instead of resuming stale code.
Thread names stay stable; DAP events carry running, stopped and exited states.
Capture waits use progress events when the editor supports them and end when
the wait or run finishes. Already-completed captures do not announce a wait.
Normal completion reports exit code 0, ends all thread/progress states, and
then terminates the session. A closed session is not by itself a program failure.
Stepping stops before the next eligible Kedi statement, so assignments become visible at the following stop. Internal model observations do not create extra same-line steps unless their event filters are enabled. Editors may clear Variables after completion; stop before the final return to inspect values.
Source edits require restart. There is no remote attach, notebook/REPL debugging, reverse stepping, variable mutation, arbitrary Python evaluation, terminal input, or async subagent-internal stepping. Tools still obey approval policy; DAP does not provide an interactive terminal for approval prompts.