Changelog
What changed in each release of @tlockney/cdp-kernel (npm and JSR). Issue and pull request numbers refer to tlockney/cdp-kernel.
Unreleased
- An interrupt on the browser target cancels the host requests in flight: the page's handler sees its
AbortSignalabort, and the Worker'shost.requestrejects with anAbortError. Before, a cell waiting on a request ended, but the page never learned the call was dead, so a native dialog it opened stayed up (#388).
0.7.1 (2026-10-19)
- The browser target can offer cells a page's own features:
preloadmodules run in the Worker before the first cell and after each restart, and get ahostwhose requests the page answers with itshosthandler, with cancellation through anAbortSignal. Acapabilitiesoption is returned inkernel_info_reply, andnotifyHostsends the Worker events (#384). - The debugger no longer stops on an exception thrown in Deno's or Node's own built-in code (
ext:andnode:scripts), as Chrome's ignore list does. With "Caught Exceptions" on, Deno's lazy setup ofnode:processthrew and caught one before the cell's own exception and could leave the cell waiting. Stepping also skips over that code (#380). - Bun, workerd and celld targets that never become ready are stopped after 60 s, as Node and Deno targets already were, and the first cell reports the failure instead of waiting forever. An interrupt while a target is starting now ends the cell waiting for it (#336).
execute_request'suser_expressionsare evaluated after a cell that succeeds, andexecute_replyreturns each one's MIME bundle, or the error it threw, as ipykernel does. They were ignored before.kernel_info_reply'slanguage_info.versionis the installed TypeScript's version instead of"5"(#340).- Starting a workerd kernel no longer trusts other users' entries in the temp directory when it cleans up after dead kernels: it skips any
cdpk-workerd-*directory that isn't a private one of this user's, so a plantedowner.jsoncan't make it kill another process (on macOS) or stop the kernel starting. A cleanup that fails is ignored (#335). cdp-kernel install --nameaccepts only the namesuninstalldoes (letters, digits,.,_and-, not.or..), so a name with a path separator can't writekernel.jsonoutside the kernels directory (#338).- A message from a Node or Deno inspector, or from the workerd or celld driver, that isn't JSON no longer crashes the kernel: the target's pending calls fail and it restarts on the next cell (#337).
- The JupyterLab debugger writes its cell files only into a temp directory that is the kernel user's own and closed to others. If another user made
cdp-kernel_<pid>first, the kernel skips the files (the frontend has the cells anyway) instead of following symlinks planted there, and leaves that directory alone at exit (#339).
0.7.0 (2026-10-19)
- Experimental: the kernel runs in a web page, with cells in a Web Worker and
npm:imports from esm.sh.scripts/build-browser.mjsbundles it; The browser says what works. The kernel's message handling moved tosrc/kernel-core.ts, which has no Node imports; the kernel process is unchanged. - The npm package exports the browser kernel:
@tlockney/cdp-kernel/browser(startBrowserKernel, typed) and@tlockney/cdp-kernel/browser/worker(the cells' Worker script), built byscripts/build-browser.mjsduringnpm run build. Not on JSR yet (#333). - A cell's error traceback points at cells as
In[n]and at the line you wrote, TypeScript types included, instead of<anonymous>and the line of the stripped JavaScript; that holds for a function an earlier cell defined. The error message's metadata also carries the traceback as structured frames (file, line, function, source lines around the failing one, the cell's execution count andcellId, and alibraryflag), under nteract'sapplication/vnd.nteract.traceback+json. Cells now run under a//# sourceURLof$cdpk$cell-<hash>.ts, which is what a stack read in code shows (#322). - A debug console expression that throws before its first
await(a typo is enough) no longer occasionally fails with "Promise was collected" on Deno. The expression now yields one task before running, as cells do (#329). - Vega and Vega-Lite charts come with a
text/plainsummary: the mark, each encoded field with its type and value range, the title and the row count, with a line per layer, view or facet panel. Frontends that can't draw the chart (jupyter console, nbconvert to text, agents reading the notebook) now show something useful. DataFrames get the same: row and column counts and each column's type (#321). - The acquisition scrubber in the MoMA example notebooks renders in JupyterLab. Its widget module exported a function taking
{ model, el }, which anywidget calls as a factory with no arguments, so it failed with "Cannot destructure property 'model' of 'undefined'". The docs site's widget replay now treats a default-exported function the same way (#327). - The landing page has a "Notebooks without Python" section linking to Ganymede, a Rust Jupyter server that serves JupyterLab with no Python installed (#325).
- CI runs the browser target's test (
test/browser.test.ts) in Playwright's Chromium, WebKit and Firefox; until now it was skipped everywhere.CDPK_BROWSERpicks the engine when running it locally (#319). - With the debugger's "caught exceptions" filter on, a debug console expression that throws (a typo is enough) no longer pauses the target in the kernel's own call. On Node the request hung until Continue; on Deno the pause ended by itself and the runtime then ran no timers or immediates, so every later cell hung. The kernel's own inspector calls (debug console, rich variable view, Shift-Tab, error descriptions) now run with exception pauses held off (#307).
- Releases are cut by starting the
publishworkflow onmainwith a version; it tags the commit once CI passes, then publishes. Thereleaseworkflow is gone: the publish run it started hadgithub-actions[bot]as its actor, which JSR refused (#315). - Notebook output on the docs site reads in dark mode: the MoMA example's wall labels set their own text color, SVG charts sit on a light panel, and the scrubber widget's year labels follow the page's text color (#312).
0.6.0 (2026-10-18)
- Node and Deno targets no longer keep the data of every comm message from the frontend, so dragging a widget's slider for a while doesn't grow the runtime. V8 kept each message as compiled source; messages now reach the target over its stdin (#287).
- Node and Deno targets no longer keep every cell's result and thrown error in memory. The inspector held each one it returned until released, so a session that showed large values (arrays, images) grew without bound; the kernel now releases them once the cell's output is shown (#286).
- The kernel starts its runtime only after it has bound all five ports from the connection file. A Deno runtime binds its inspector to a random port within milliseconds of starting and could take one of them, so the kernel died at startup with
EADDRINUSE(#257). - Deno's "Debugger session started." notice no longer shows up in the first cell's output when it arrives late (#281).
- Cells on Node and Deno that show output (a result,
display(),console.log) no longer wait ~40 ms for the inspector socket: a trivial cell takes 7 ms instead of 48 ms on Node. Messages from the target now travel on its stdout, and the session that runs cells receives no inspector events (#271). - A Node or Deno runtime could stop running
setTimeoutcallbacks for good, leaving the kernel waiting on a cell or comm message forever. A kernel request that arrived while the runtime's own timer code ran could make the runtime lose its 1 ms timer list. Cells and comm handlers now yield withsetImmediateinstead (#237). - Completion knows declared types: TypeScript's language service runs over the executed cells plus the one being edited, so
x.lists a typed value's members even before it has a value, completion works inside expressions ([1, 2].map((n) => n.toF), andnpm:/jsr:imports complete from their declarations. Runtime-only names are still listed after the typed ones, and JupyterLab shows each match's kind. The service runs in a worker thread started on the first completion (that first request is answered from the runtime alone) and ended after 10 minutes without one; it takes about 150 MB while running. Node targets get@types/nodefrom the notebook's project, or installed once into the per-user npm cache.--no-language-serviceturns it off.typescriptis now a dependency (#273). - Shift-Tab inspection shows the declared type, signature and JSDoc from the same language service, then the runtime's value preview and properties. It also works on names that have no value yet and on members of expressions the runtime can't evaluate (#274).
- Typed completion and inspection know each target's globals:
deno typeson Deno,@types/bunon Bun, and on workerd and celld a Workers project'sworker-configuration.d.ts(fromwrangler types), else@cloudflare/workers-types. On Deno, imports an executed cell made (npm:,jsr:, URLs, the project's import map) are typed from Deno's own cache, found withdeno info; nothing new is fetched for completion. On Bun,npm:/jsr:types come from Bun's install cache (#275). - The docs describe typed completion and inspection, where types come from, their cost and limits, and
--no-language-service(#276). - Node 22.18+ is supported as the host and as a target;
engineswas>=24. 22.18 is the first Node 22 that strips TypeScript types without a flag, which imported.tsfiles rely on. CI adds Node 22 on Linux and macOS. The one difference found: Node 22 formats symbol keys as{ [Symbol(k)]: 1 }(#264). kernel_info_requeston shell is answered without waiting for the runtime to start, so clients probing during a slow start get one reply instead of queuing more. Until the runtime is up, the banner leaves out its versions.interrupt_replynow waits at most 1 s for the runtime, under the 5 s clients allow control requests (#253).- The kernel exits, stopping its runtime, when the server named by
JPY_PARENT_PIDexits, as ipykernel does, so a server that is killed or crashes no longer leaves kernels running. Without the variable (a kernel started by hand) nothing changes; on Windows it is ignored (#254). cdp-kernel listandcdp-kernel uninstall [NAME...] [--all]list and remove the kernelspecs cdp-kernel installed, without Jupyter. They use the same kernels directory asinstalland leave other kernels' kernelspecs alone (#249).--type-strip sucrasestrips cells' types with sucrase, plain JavaScript, instead of esbuild's native binary, and an embedder can pass its own type stripper. sucrase is an optional dependency, loaded only when picked; it rejects decorators,accessorfields and namespaces (#234, #238).- A kernel that is SIGKILLed no longer leaves runtimes behind: a helper process started with the kernel kills the runtimes it registered, and removes their temp directories, when the kernel dies. This covers celld and a Node or Deno runtime busy in a cell (#240), and Miniflare's workerd (#241).
- When the kernel can't bind a connection-file port, it writes what holds the port (sockets and their processes, from
/procon Linux) to stderr (#257). - The debugger moves a breakpoint off a line the type stripper blanked to the next line that runs (#239), and esbuild is loaded only when a cell needs it (#238).
- A container image with JupyterLab and every runtime (#267), published to
ghcr.io/tlockney/cdp-kernelwith each release (#269), a devcontainer built from it (#268), and Binder and Codespaces links (#270). - Docs: Ganymede as a way to run notebooks without Python (#250), what VS Code needs to draw widgets (#228), the support policy and 1.0 checklist (#261), and
CONTRIBUTING.md,SECURITY.mdand issue templates (#262). - CI runs the tests nightly against the latest runtime releases (#259), adds a soak test of one long session per runtime (#260), and benchmarks startup and per-cell latency (#265). Releases can be cut from the Actions tab with the
releaseworkflow (#235).
0.5.0 (2026-10-16)
Changes in behavior:
- The npm and Bun package caches follow
XDG_CACHE_HOME, as the runtime cache already did. With it set, packages are downloaded again into the new location, and~/.cache/cdp-kernel/{npm,bun}can be deleted (#163). - On Bun,
consoleoutput is formatted as on Node, and stdout and stderr writes are synchronous, so a large write beforeprompt()no longer deadlocks (#180). stop_on_erroris honoured: execute requests queued behind a failed cell are aborted instead of run (#123).process.exit()orDeno.exit()in a cell ends the runtime, and the next cell starts a fresh one (#130).
New:
- On Node,
.ts,.tsxand.jsxfiles imported by a cell are compiled with esbuild when Node's type stripping can't handle them (enums, namespaces, parameter properties, JSX) (#73, #91). - Cells can use standard decorators and
accessorfields (#142), anonymousexport default class/functionandimport x = require()(#143). --deno-compatthrows a clear error for aDenomember it doesn't implement (#172).- Documentation site at cdp-kernel.org/docs, with getting started, runtime, troubleshooting, security, VS Code and CLI reference pages, and this changelog.
Jupyter protocol:
- Interrupting a cell blocked in
prompt()makesprompt()throw instead of wedging the kernel, and a staleinput_replyno longer answers the next prompt (#121). - Malformed JSON from a cell or the stdin port no longer crashes the kernel (#122); a shell handler that throws replies with an error (#125).
- A silent request publishes no
execute_input, a shellshutdown_requestis handled, and control requests publish busy/idle (#124). - A frontend-opened comm whose
comm_openhandler throws is dropped (#137).
Fixes:
- Runtimes no longer outlive the kernel: they are killed when it exits by SIGTERM, SIGHUP, an uncaught error or shutdown during startup (#127, #218); Node and Deno targets exit when their kernel is SIGKILLed (#219); workerd processes and directories left by a dead kernel are cleaned up (#220); workerd is SIGKILLed when SIGTERM doesn't stop it (#128).
- Deadlocks:
prompt()after a large write on Node (#131), a display payloadJSON.stringifyrejects (#129), and requests on a closed session (#133). An interrupt during result formatting reportsInterrupted(#135). - Fewer startup failures: a runtime whose start fails or stalls is stopped (#52); intermittent "Promise was collected" errors are fixed (#50, #55); celld retries a start that loses a port race; a kernel port briefly in use at startup is retried (#160); a start whose bootstrap never installs fails with an error (#134).
- Cell results: a trailing sequence expression returns its last value on Bun, workerd and celld (#140); a trailing side-effect or type-only import is not the result (#139); stack-trace line numbers no longer shift (#150); the completeness check handles escaped backticks and regex literals (#126);
.ts/.tsximports with a query or fragment are compiled (#145). - workerd and celld: a module that threw while loading is re-sent (#147); errors from timers and rejections after a cell are reported (#148);
npm:/jsr:installs no longer block heartbeat and interrupt (#149). - Deno target:
Deno.completes and Shift+Tab inspectsDeno.*again (#132). cdp-kernel.lock.jsonis written atomically, a live install lock is no longer stolen, and a corrupt lockfile names its path (#144).- Runtime stderr notices are filtered per line, so user lines starting with "Download" are kept (#136); WebSocket fragmented binary messages and Close frames are handled (#138); cleanup of buffers, debugger files, token comparison and image errors (#152).
- npm installs run through
cmd.exeon Windows (#51). Windows is still not supported. - LICENSE credits the MurmurHash2 port's JupyterLab and Gary Court licenses (#154).
- CI tests Deno 2.x (latest) only, pins Bun 1.4, and smoke-tests the published JSR package after each release (#146).
0.4.0 (2026-10-15)
- JupyterLab's visual debugger on Node and Deno: breakpoints, stepping, variables and pause on exceptions, over CDP (#18).
- Inspection tooltips (Shift+Tab) and input history (#10).
- An opt-in
cdp-kernel.lock.jsonpinsnpm:/jsr:versions per notebook, and installs are serialized (#14). npm:installs save exact versions, so a range stays at what it first resolved to, andjsr:works with npm 12 (#39, #40). Caches written by 0.3.0 or earlier can be deleted to start fresh.- JSR kernelspecs start on release day despite Deno's minimum dependency age (#12).
- CI covers Node 24 and 26 with Deno 2.9 and the latest 2.x, on Linux and macOS (#15).
- A MoMA collection example notebook.
0.3.0 (2026-10-14)
- New targets: Bun (#7), workerd (#8) and celld (#25).
- In a Workers project, cells get the project's bindings as
env(#26).
0.2.0 (2026-10-14)
- Jupyter comms and anywidget-style widgets with
jupyter.widget(#9). - Deno targets honour the notebook's Deno project config (#13).
- Type declarations for the
jupyterglobal (#11). kernel_inforeports the real version, and the inspector's URL is kept private (#22).
0.1.1 (2026-10-14)
First release on npm and JSR. A Jupyter kernel ported from deno jupyter (v2.9.6) that runs cells on Node or Deno over the Chrome DevTools Protocol: the jupyter display API, TypeScript, npm: and jsr: imports, interrupt, prompt()/confirm(), and an opt-in Deno namespace for Node.