Docs

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:

ActionKey
ContinueF5Runs on to the next breakpoint
Step OverF10Next line, without descending into calls
Step IntoF11Into the function being called
Step OutShift-F11Finish this function and stop at the caller
StopEnds 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.