v2.3.10
Pre-release
Pre-release
Highlights
- Motion tools wait for the real stop.
continue_execution,step_over,step_into,step_outandpause_executionused to settle on VS Code's active-stack-item change — which also fires when the frame is cleared on resume — and could report a stop with an empty location while the target was running. They now arm a DAPstoppedwaiter before the request goes out and open the result with the stop reason. - One pack root, agreed with the CMSIS Solution extension.
%LOCALAPPDATA%\Arm\Packson Windows; next to the CMSIS Solution extension its owngetPackRootPathanswer, asked once per window, carried on every target resolution and shared with the SVD lookup — solist_target_docsandlookup_peripherallook in the same directory. - Containment.
fetch_docrefuses loopback, link-local, private and cloud-metadata addresses and re-checks every redirect hop;get_build_diagnostics { file }reads inside the open workspace only. - Multi-window robustness. UTF-8-safe control-channel bodies with size caps, atomic registry and agent-configuration writes (
~/.claude.jsonis re-read before every write), bounded shutdown, serial ports released by the window that owns them, a pyOCD flash that ignores SIGTERM is now killed. - Documentation tools.
maxPdfMbonread_doc_pagesand imports, ambiguous short ids reported instead of guessed, user-document ids carry their scope folder (user/keil/stm32u5xx-dfp/rm0456), a shared timeout fence that never leaves an unhandled rejection. - The whole set comes from a codebase review (
docs/codebase-review-2026-09.md, phases 0–2 of its plan) with 66 new tests: 491 passing, both transport suites, lint and type-check clean.
[2.3.10] - 2026-09-07
Added
- Tests for the DNS-rebinding guard (
isLoopbackHostHeader/isLoopbackOrigin), the hardware timeout wrapper, and the pdf.js worker's positive timeout, restart, idle retirement and dispose-while-pending paths (through an injectable stand-in worker). - The Pack Docs panel can clear its store — in two separate steps, each behind a confirmation. Clear extracted text… removes every document's extracted pages, metadata and search index (
*.pages.jsonl,*.meta.json,*.idx.json) across the store and prunes the emptied directories; downloaded PDFs and fetch records stay, so fetched documents remain fetched and every document is extracted and indexed again on its next use. Delete downloaded PDFs… removes whatfetch_docdownloaded — thearm/andweb/trees with their PDFs, fetch records and extraction — so those documents are offered as "not fetched" again. The modal confirmation names the document count, file count and size it is about to remove; the in-memory page, index and chapter caches are dropped with the files.PageStoregainsstoreUsage(),clearExtracted()andclearDownloads(), tested.
Changed
- The Pack Docs panel is laid out by what each tab shows. The target picker — a cbuild-run context or pack + device — moved into a header shared by every tab, with the resolution and core under it, since it drives them all. The former Target tab, which stacked the SVD browser above the document list, is split into Documents (every document the tools see for the target, its state and the fetch / index / browse / search actions) and Peripherals (the device SVD, the Arm core peripherals and the NPU as groups, instances and bit views); Store is Page store (what is extracted and indexed on disk, across all targets) and Tools stays. Each tab carries a count and a tooltip, opens with a one-line description of what it shows, and the panel no longer refers to "the Target tab" in the other tabs. A saved tab from the old layout opens as Documents.
Changed
- User-document ids name their scope folder (
user/keil/stm32u5xx-dfp/rm0456,user/devices/stm32u5/errata,user/boards/b-u585i-iot02a/schematic), so the same file name attributed to two scopes no longer collides or swaps ids between calls; a document at the root of the user folder keepsuser/<name>.read_doc_pagesand thedocsfilter still accept the bare file name when it is unique. Extracted text and indexes are unaffected. Tested. - A short document id that matches several documents is reported as ambiguous by
read_doc_pagesandfetch_doc—Document id 'rm' is ambiguous — it matches …— instead of silently reading pages of whichever the listing yielded first. A unique trailing segment (test-rmforstm32f7xx-dfp/test-rm) and a case difference still resolve. Tested. resettells the truth about the end state. When the last (or only) reset method is unsupported the result no longer says "trying the next method"; when the reset could not be verified the result says the target is halted, that it was running before when the tool halted it, and thathalt: falsewas not applied — an unverified target is never resumed.ResetOutcomegainsresumed; the rendering moved toresetAssist.ts. Tested.get_threadsreads top frames for the first 32 threads in batches of four instead of onestackTracerequest per RTOS task fired at once — a GDB server serialises DAP requests, so a 60-task build made every request wait on the others and hit the deadline together.cmsis_action's post-connect session check fits its advertised window. The two thread probes afterload_and_debug/attachwere fixed at t+3 s and t+6 s with 5 s each, up to 16 s past an "opportunistic" 8 s wait; they are now scheduled at 35 % and 75 % of that window.probeScheduleis exported and tested.
Fixed
fetch_docno longer reaches local or private addresses. A pdsc<book url>or afetch_doc { url }pointing at loopback, link-local, RFC 1918, CGNAT, multicast or cloud-metadata addresses (127.0.0.1,localhost,169.254.169.254,10.x,192.168.x,fe80::, …) is refused before any request is made, and redirects are followed one hop at a time with the same check on every target, so a public URL cannot bounce the download onto the MCP or control port. The error names the workspace docs folder as the way to use such a document. A public name that resolves to a private address is not caught (no DNS lookup).PackDocsHost.allowPrivateHostslets a loopback fixture through in tests. Tested.packDocs.maxPdfMbapplies toread_doc_pagesand to imported documents too, not only to search and the index command; an oversize document is reported with the setting to raise instead of being extracted anyway. Tested.- A window that opened a serial port through
serial_openreleases it when the window closes, whether or not it was the router. Serial teardown moved from the MCP server (router window only) to the per-window coordinator, bounded to two seconds so a wedged tty cannot holddeactivate. Tested. - Closing a window no longer waits on open MCP or control connections. The HTTP servers close idle sockets at once and destroy the rest after two seconds, so an in-flight tool call or an open notification stream cannot hold the extension host in
deactivate; a window that failed to bind the router port releases the partially started server. Tested. serial_closeafter the adapter was unplugged no longer wedgesserial_openwith "already open". The controller forgets the port even when closing it fails. Tested.read_peripheral_registerhonoursdapRequestTimeoutMsand the call'stimeoutMsinstead of a fixed 10 s per DAP request, and the GDB evaluate fallbacks used byread_memoryforward the same deadline as the DAP attempt. Tested.- An MCP session whose
initializefailed mid-way no longer leaks its transport and server objects. - Agent configuration files (
~/.claude.json, …) are re-read immediately before they are written and the update is retried when another process changed the file in between, so a Claude Code write during setup is no longer lost; an unchanged file is not rewritten at all.src/utils/jsonFileRewrite.ts, tested. - Removed an upstream
coreclrspecial case instart_debuggingthat opened the program file and fired the test-explorer debug command without awaiting it. continue_execution,step_over,step_into,step_outandpause_executionno longer report a stop with an empty location while the target is still running. They settled on VS Code's active-stack-item change, which also fires when the frame is cleared on resume, and thecontinuedevent of the request just sent usually arrived after the listener was attached. They now wait for the DAPstoppedevent — the signalwait_for_stopandresetalready use — armed before the request goes out so a stop that lands during the round trip is not missed, and the result opens with the stop reason (Target stopped (reason: breakpoint)). The timeout recovery pauses the same way.IDebuggingExecutorgainsarmStopWaiter. Tested with a scripted executor.- A pyOCD flash run that ignored SIGTERM at the deadline was never killed and
flashhung. The SIGKILL escalation checkedchild.killed, which is true as soon as the SIGTERM was sent; it now checks that the process has not exited.flashWithPyocdtakes an injectablespawnand grace period. Tested with a child that ignores SIGTERM. - Tool results forwarded between VS Code windows could arrive with
�in place of emoji or CJK text. The router and each window's control server appended body chunks to a string, so a multibyte UTF-8 sequence split across two chunks was decoded in halves; both now collect the raw bytes and decode once. They also refuse bodies above 1 MiB (requests) or 16 MiB (results) instead of buffering without limit, the MCP endpoint's body limit is an explicit 1 MiB, and a client that aborts mid-request no longer leaves the control server's response open. Tested with a request and a reply split inside a four-byte character. - A pdsc containing an out-of-range numeric character reference (
�) madelist_target_docsfail with "Invalid code point". Such references are now left as written, like unknown named entities. Tested. - A disk error while
fetch_docwas writing a download (full disk, unwritable page store) crashed the extension host or left the call hanging. The write stream's error is observed from the first byte and raced against the back-pressure wait, so it becomes the tool's error and the partial file is removed. Tested with an unwritable.partpath. - A documentation or build-artefact call that failed after its timeout had already been reported left an unhandled promise rejection in the extension host. The two handlers' identical timeout fences are now one helper (
src/core/toolRun.ts) that keeps observing the call and logs its late outcome. Tested. - A VS Code window could lose its entry in the multi-window registry while another window was reading it. Registrations were written in place and any file that failed to parse was deleted; they are now written to a temp file and renamed (
src/utils/atomicFile.ts), the heartbeat rewrites the window's own copy instead of re-reading the file, and an unparsable file is only removed once it is older than the staleness window. Tested. - Two VS Code windows syncing skills at the same time could delete each other's in-progress copy, failing the install in one of them. The stale-staging sweep removed every
.<skill>.tmp-<pid>directory; it now skips those whose process is still alive. Tested. - Two VS Code windows writing an agent configuration at the same time could clobber each other's temp file, and the Codex
config.tomlwas written in place rather than atomically. Every agent configuration write goes through the same temp-file-plus-rename helper with a per-process unique temp name. get_build_diagnostics { file }reads build logs inside the open workspace only. An absolute or..path outside every workspace folder is refused, and a file that carries no compiler or cbuild output is reported as "does not look like a build log" instead of being rendered — the tool was an arbitrary-file read for the agent. Tested.- A zero or negative pdf.js timeout is decided synchronously instead of on a zero timer. A zero timer still raced the worker's reply: a warm thread answers a small document within one Windows scheduler tick, so the "timed out" rejection the page-store test expects was missing on the windows-11-arm runner. A timeout of zero or less now rejects and terminates the worker right after the request is posted.
- On Windows the documentation tools looked for packs under
%USERPROFILE%\.cache\arm\packs, solist_target_docsreported every pack as "not installed". The CMSIS-Toolbox default pack root is%LOCALAPPDATA%\Arm\Packson Windows and~/.cache/arm/packsonly on Linux and macOS;defaultPackRootnow follows the platform (falling back to<home>\AppData\Local\Arm\PackswhenLOCALAPPDATAis unset), andCMSIS_PACK_ROOTstill overrides it. The${CMSIS_PACK_ROOT}expansion insvdFilepaths shares the same resolution instead of its own copy of the Linux path. Tested for all three platforms. Next to the CMSIS Solution extension the tools no longer guess at all: before every target resolution they ask it for its pack root (cmsis-csolution.getPackRootPath, the sameCMSIS_PACK_ROOT-or-OS-default the panel builds with) and adopt the answer, so both extensions look in the same directory and a "not installed" note names the one the panel would install into.PackDocsHostgains the optionalpackRootFromToolchainhook; an absent, slow, empty or failing answer keeps the platform default. The extension is asked once per window (again after an extension or settings change; an unanswered ask is retried after a minute) and the output channel reports which root was adopted. Every consumer — the document list, the SVD lookup, the CMSIS-Core and NPU headers — reads the root the resolution was made under (TargetResolution.packRoot), so the first call in a window no longer looks in the platform default while the answer is still in flight; andlookup_peripheral,read_peripheral_registeranddiagnose_faultexpand${CMSIS_PACK_ROOT}insvdFileand cbuild-run entries with that same root. Tested. - Document indexing failed next to the CMSIS csolution extension: "The
Array.prototypecontains unexpected enumerable property "groupedBy"". pdf.js refuses to start on a realm whoseArray.prototypeorObject.prototypehas an enumerable property, and VS Code's extension host is one process shared by every extension — the csolution extension assignsArray.prototype.groupedBy, so every PDF was skipped andsearch_target_docsfound nothing indexed. pdf.js now runs on aworker_threadsworker of its own (dist/pdfWorker.js, a second esbuild entry point): a fresh realm no other extension can patch. The thread is started on the first document, kept for the next, retired after a minute idle, and terminated when an extraction times out — which now actually stops pdf.js mid-document instead of waiting for the next page. The bundledpdf.worker.min.mjsstill ships for the thread's fake worker;itemsToPageTextmoved topdfText.ts. Tested with a patchedArray.prototype, a timeout followed by a fresh extraction, and a missing file.
Pre-release (odd minor). Full changelog: https://github.com/MatthiasHertelArm/CMSIS-Developer-Assistant/blob/v2.3.10/CHANGELOG.md — previous pre-release: https://github.com/MatthiasHertelArm/CMSIS-Developer-Assistant/releases/tag/v2.3.9 (documentation and build-artefact tools built in) — based on Open-CMSIS-Pack main after PR Open-CMSIS-Pack#30 (2.3.9) merged.