v1.0.45
⚡ V40: Performance-First Execution — 4 specs, zero regressions
Surgical I/O, CPU, and memory optimization across 4 fronts. Result: faster sessions, lighter startup, leaner history.
Session I/O (spec 420)
- Incremental writes:
appendFileSyncreplaces rewriting the entire messages file - Sessions index cache:
_cachedSessionsIndexin memory —loadSessionsIndex()was called 6× per turn reading from disk - Directory guard:
_projectDirEnsuredavoids unnecessarymkdirSync - String buffers:
push+joinin streaming loops instead of+=(reallocation on every chunk)
Startup (spec 430)
- Parallel skills:
Promise.all+fs/promises— simultaneous loading, zero sequentialreadFileSync - Cached templates: Prompt templates (
templates/tools/*.md,templates/skills/*.md) in immutable cache — no longer re-read from disk each turn
Compaction & memory (spec 440)
- Incremental hash:
findStablePrefixEndIndex()uses a single incremental SHA-256 instance — O(N) instead of O(N²) - Parallel turns:
readRecentTurns()decompresses files in parallel withPromise.all - Async backup:
backupSpecFile()usesfs/promises.copyFile— zero blocking
Hardening (spec 450)
- Bounded concurrency:
readRecentTurnsprocesses in batches of 8 with early bailout — no wasted I/O - mtime-based invalidation: Sessions index cache checks
mtimeMs— safe for multi-terminal use - ENOENT recovery:
ensureProjectDirresets the flag if.dscode/is deleted mid-session - ESLint
no-floating-promises: Rule active — 5 violations fixed withvoid
🐛 PDF: Context Budget Fix (spec 460)
- PDFs with compressed ObjStm:
countPdfPagesreturnsnull(not0) when the regex heuristic fails. Large PDFs are no longer embedded as base64 in context — preventing 1M token window overflow. - PDF read handler uses
followUpMessages: Output is a short descriptive string instead of inline base64. Content goes throughcontentParamsasimage_url, matching the image handler pattern. Prevents context pollution. - Functional
pagesparameter:pagesnow extracts only the requested page range (viapdf-lib) instead of encoding the entire file. Falls back to full file with clear error metadata on extraction failure. - 5 new tests: Covers descriptive output, followUpMessages, page extraction, fallback on corruption, and single-page extraction.
🚀 Optimizations with native Node.js 24 APIs
- Grep handler: native
fs.globSync, async parallel reads, streaming — -143 lines, -1 dependency - Glob handler: custom walker replaced by
fs.globSync— -51 lines
🔧 Fixes
cacheModein Zod schema: Settings withcacheModeare no longer rejected as invalid/spec-pipe: Auto-creates session when none is active- FD leaks: File descriptors closed in grep binary detection catch and MCP client disconnect
- Unused variable: Regex
unusedInBinaryDetectionremoved from grep handler
📋 Documentation & infra
- 5 steering rules in
AGENTS.md: authorization, cross-check, verify, consequence, output - V39 and V40 documented in
vision.md - Node 26 notice on welcome screen: "Starting October 2026, DsCode will require Node.js 26."
- Release notes now use
RELEASE_NOTES.md(not--generate-notes)
🚀 Node.js 24 — All-in
Full migration to Node 24 as baseline. Zero backward compatibility.
Native APIs replacing dependencies
- Native
fs.globSyncreplaces npmglobpackage — -4 dependencies - Native
node:zstdreplaces Brotli fallback fromnode:zlib— 4× smaller compressor Error.isError()→getErrorMessage()cross-realm safe in 21 files- Native
structuredClone— 8-line deep clone becomes 1 - esbuild target
node24— no polyfills for Node 22 - CI on Node 24 — build and test on the real runtime
🍎 macOS Apple Silicon in automatic releases
- macOS ARM64 (
macos-latest) now builds automatically on every tag push - macOS Intel (
macos-13) removed — deprecated runner by GitHub, no queue wait - Dry-run covers Windows, Linux, and macOS ARM64
- Checksum download fixed (cause of
400 Bad Content-Lengtherror in v1.0.41)
🔄 Robust auto-update
- Asset naming 100% aligned between CI and
update-check.ts - Portable packages (fallback when SEA fails) now copy all companion files:
dscode.mjs,node,templates/,node_modules/ - File extraction and atomic binary replacement across all platforms
🖼️ Local OCR with Tesseract.js
- Offline OCR via
tesseract.jsfor models without image support (e.g., DeepSeek V4) - Dynamic import —
tesseract.jsonly loads when OCR is actually used, zero startup impact - All 12 transitive dependencies packaged in the portable bundle
- Extracted text truncated at 2000 characters (word boundary)
/image-pasteand/image-uploadwith automatic OCR fallback- Drag-and-drop files via paste in terminal
🐛 Fixes (from previous versions)
- v1.0.41:
400 Bad Content-Lengtherror on publish — checksums were not downloaded - v1.0.42/43: macOS Intel blocked releases due to missing runner — removed from pipeline
- Auto-update: Portable packages broke on update — now copies companion files
- Bundle: Silent build failure — now
exit(1)and CI detects it - OCR startup:
regenerator-runtimenot found at startup —tesseract.jsloaded on demand - ErrorBanner in Ink, context window overflow, spec suffixes
📐 Specifications and build
- Specs 370-410: build validation, operational resilience, traceability, auto-update
validate-binary.mjsuses tag version (not package.json)release-dry-run.ymlcovers 3 platforms- README URL validation in CI