Debugging Automations
Automations run in a real Deno process, and you can stop that process on a line and look around — breakpoints, stepping, the call stack, and a console that evaluates in the paused frame.
Dev preview — not stable. Tranquil is in early, active development. Everything here — APIs, interfaces, and behavior — is provisional and will change before a stable release, often without notice or backward compatibility. Don’t build anything you depend on against it.
Set a breakpoint
Click the narrow column to the left of the line numbers in any .ts file, or put the cursor on a
line and press F9. A red dot marks the line. Click it again to remove it.
Breakpoints are saved, so they survive closing the file and restarting the app. Debug: Clear All
Breakpoints in the palette removes them everywhere, and there’s a button for it on the .ts tab
bar.
Start a session
Press the debug button on the .ts file’s tab bar, or run Debug: Debug Automation from the
palette. The script starts paused and runs until it reaches one of your breakpoints.
Save the file first. A run executes what’s on disk, so breakpoints set in an unsaved buffer sit on lines the debugger never sees — the session runs the previous version and stops somewhere else, or nowhere at all.
The transport controls replace the Run button while a session is live:
| Action | Key | |
|---|---|---|
| Continue | F5 | Runs on to the next breakpoint |
| Step Over | F10 | Next line, without descending into calls |
| Step Into | F11 | Into the function being called |
| Step Out | Shift-F11 | Finish this function and stop at the caller |
| Stop | — | Ends the session and kills the process |
These keys work while an editor has focus. They’re deliberately not global: a browser tab already
uses F5 to reload and F12 for its own DevTools, and Windows and Linux bind F11 to full screen.
The debug pane
Opens in the left dock when a session starts — Debug: Toggle Debug Panel shows it any time. Four sections, each resizable by dragging the divider between them:
Call Stack — the frames that led here. Click one to inspect that frame; the editor jumps to its line and Variables re-reads in that scope.
Variables — everything in scope, expandable. Your own values come first; imports sink to the
bottom, and declarations the run hasn’t reached yet are dimmed, because a const that hasn’t
executed reports the same undefined as a variable genuinely holding nothing. Large collections are
chunked into [0 … 99] ranges rather than rendering thousands of rows.
Watch — expressions re-evaluated at every stop. They expand like Variables, and keep their expansion as you step.
Console — evaluates in the paused frame, so locals resolve the way they do on that line.
The line that runs isn’t always the line you clicked
Deno rewrites TypeScript rather than stripping types in place, so generated line numbers drift from your source — blank lines vanish, multi-line calls get re-flowed. Tranquil maps back through the source map, and if a breakpoint binds somewhere other than where you clicked, the dot moves to where it actually bound. A breakpoint on a blank line slides to the next real statement.
A breakpoint in code that never runs stays hollow and greys out: that’s the debugger saying it looked and found nothing to bind to.
Code inside tab.evaluate() runs in the page
tab.evaluate(() => …) serializes that callback and executes it inside the web page, not in
your script’s process. The automation debugger is attached to the Deno process, so it cannot stop
there — Tranquil refuses a breakpoint inside one of those callbacks rather than letting you set a
dot that would never hit.
To debug page code, use a debugger; statement inside the callback and open the browser pane’s
DevTools:
// @permissions browser
import { tabs } from "tranquil/automation";
const tab = await tabs.active();
await tab.evaluate(() => {
debugger; // stops in the PAGE's DevTools
return document.title;
}); debugger; also works in your own script, where it stops in the debug pane like a breakpoint. It’s
inert when nothing is attached, so a normal run walks straight past it — you can leave it in while
you work.
While a session is live
The Run button is hidden: starting an ordinary run of a script you’re stepping through would spawn a second process against the same browser tab. Stop the session and it comes back.
Debug always runs the whole file, even with text selected — a selection runs as its own temporary module, so your breakpoints would belong to a file that isn’t executing.
Output still goes to the Automation Runs panel (⇧⌘U), and a paused run shows there as paused rather than running. Cancelling from that panel kills a paused process, which is the right escape
hatch if a session gets stuck.
Next: Performance & the Main Thread — why a working script can still freeze the page it’s driving.