Skip to content

Releases: levelcodeai/levelcode

LevelCode v1.0.4

Choose a tag to compare

@github-actionsgithub-actions released this 29 Jul 02:28
7fb140f

LevelCode v1.0.4

The big one: the agent can now use external tools over MCP — a filesystem server, GitHub, Postgres, an internal company server — alongside its own built-in tools, with a security model that treats an MCP server as exactly what it is: arbitrary code that runs with your privileges. Plus autopilot now honors a changed step limit live, and the agent's activity timeline reads as one clean thread.

Highlights

Bring your own tools, over MCP

Point LevelCode at any Model Context Protocol server and its tools join the agent's own — namespaced server__tool, usable by every model LevelCode supports, with no code to write. This first release speaks stdio (a server you run as a local subprocess), which covers the overwhelming majority of servers and sidesteps the remote-transport spec churn landing this year; remote/HTTP servers come later.

You declare a server in one of two places, and they are trusted differently on purpose:

  • levelcode.ai.mcp.servers (your own settings) — you typed it, so it starts.
  • .levelcode/mcp.json (committed in a repo) — read and listed, but it never starts on its own.

Security is the feature, not a footnote

Spawning an MCP server is at least as dangerous as running a shell command, so every edge is gated:

  • A launch gate for repo servers. A .levelcode/mcp.json server asks before it ever runs, showing you the literal command, arguments, and environment — no summarizing. Approve once and it's remembered, but trust is bound to a SHA-256 fingerprint of exactly what would run: change the command, an argument, or an env var and it asks again — so a repo can't get npx …server-filesystem approved and then quietly swap in curl … | sh under the same name. With no window to ask in (a headless or test context) it fails closed and simply doesn't start.
  • Every tool call asks by default, showing server · tool · arguments before it runs.
  • Autopilot doesn't relax this. MCP tools are third-party code, so autopilot still prompts for them — the only thing that grants a silent run is your own per-tool allow-list ("github__list_issues": "allow"). A server's own "this is safe" hint grants nothing on its own; its "this is destructive" hint forces a prompt regardless.

Manage MCP servers without touching JSON

A new AI: Manage MCP Servers… command (and a link at the foot of /mcp) lets you:

  • Add or remove a server through a prompt instead of hand-editing settings — it writes your global settings only, never a repo's, so adding one can never weaken the launch gate above. The arguments box takes a real command line and splits it quote-aware, so -y @modelcontextprotocol/server-filesystem "/Users/me/My Documents" stays one path.
  • Revoke trust you granted a repo server, per server or workspace-wide — the missing other half of "trust on first use," which used to be a write-once decision.
  • See a stale approval for what it is — a server whose command changed since you approved it now reads command changed — needs approval, not a bland "not started," because that gap is exactly the attack the launch gate exists to stop.

See what's configured with /mcp

/mcp lists every configured server — running or not — with its state, where it came from, its exact command, and (once live) its tools and whether each is allow-listed. It answers the two questions you actually have — why isn't my server being used? and what is this repo asking to run? — that a list of only-running servers can't. The context-usage popover now also breaks out an MCP tools line, so the standing per-turn cost of a chatty server is no longer invisible.

A cleaner agent timeline

The activity thread under each run — its tool calls, approvals, and commands — now reads as one connected line instead of disconnected stubs: the connector rail threads continuously through consecutive steps (only a stretch of narration breaks it), the way GitHub, Cursor, and Claude Code draw theirs. And an approved MCP tool call is now a single row — not an Approved · server · tool chip plus a separate 🔌 server · tool node — so it's one row per action, matching how allow-listed calls already looked.

Autopilot honors a changed step limit, live

Raising Maximum tool-use steps mid-run now takes effect on the very next step, instead of being frozen at the value from when the goal started — so bumping it from 25 to 1000 when autopilot pauses at "step limit" actually lets it keep going.

Test coverage

  • 27 suites across the bundled extensions, all green.
  • The MCP core is pure, testable modules by design: config merge + tool-name namespacing + the approval policy (mcpConfig), the launch fingerprint and trust logic, and the /mcp + manage-servers row builders are all unit-tested without spawning a process or opening the editor — in the same two-corpus style the command-danger classifier uses, where the test banner states the load-bearing direction.
  • The webview timeline changes are pinned by the chat UI's static-CSS-invariant suite (webviewCss) — e.g. the rail's connector offset can't silently drift from the row gap, and the one-row MCP fold stays wired end to end (agent.js → approval chip → run-node).

Full changelog:v1.0.3...v1.0.4

LevelCode v1.0.3

Choose a tag to compare

@github-actionsgithub-actions released this 27 Jul 04:00
a821e3c

LevelCode v1.0.3

A design release for the one screen everyone opens: the signed-in account panel is rebuilt in the levelcode.ai "classic" look — a floating-chevron hero, a model-agnostic plan pill, a Documentation link, and a primary Manage account button — followed by the fixes that make it fit and close cleanly.

Highlights

A redesigned account panel

The signed-in panel used to be a plain card: an initials avatar, a model name, a credits figure, and a paragraph explaining "gateway mode." It's now the levelcode.ai classic (atom.io) surface end to end — the same light / One Dark palette, system type, and tabular numerals as the rest of the product.

  • A floating-chevron hero. The three rising chevrons and drifting "portal" circles from the landing page open the panel in place of the initials block — brand-forward and quietly in motion (portalDrift, 7s, staggered per layer), and perfectly still for anyone who sets prefers-reduced-motion.
  • Model-agnostic. The pill reads plan · Pro+ on the gateway, or your own key on BYOK — never a specific model name (those churn, and naming one confused people about what they were paying for). The credits number is gone from here; it lives on the account page, where there's room to explain it.
  • The gateway explainer is gone. That "how the gateway works" paragraph wasn't telling you anything the routing chip in the footer doesn't. BYOK keeps a single line — that your keys never leave your machine — because there it's genuinely reassuring.
  • A Documentation link (→ levelcode.ai/ai/docs) sits in the footer beside the "Synced · settings · skills · keymaps" note. It's keyboard-operable, and like every external link in the panel it can only ever open a fixed, known destination — never a URL handed in from the page.
  • Manage account is the primary button — the one you're most likely to want — except on the free tier, where the accent belongs to Upgrade to Pro so two buttons never compete for it.

Fit and finish

The redesign shipped with three follow-ups from actually living with it:

  • The chevron no longer clips. It's smaller and top-anchored, so the hero is fully contained at every window size instead of being cut off along the top edge.
  • The × closes again. The full-bleed hero was painting over the close button and swallowing the click; it's back on top (z-index above the hero, which no longer intercepts pointer events) and closes the panel as expected — by keyboard too.
  • High-contrast themes defer to the editor's own tokens. The card maps its colors to --vscode-contrastBorder and friends under a high-contrast theme, so your VS Code accessibility choice wins instead of the card hard-coding its palette.

Test coverage

  • 24 suites across the bundled extensions, all green.
  • This is a webview change, so it's verified the way the picker and plan-card work were — by parsing the shipped chat.html (the webviewCss suite and friends) rather than by snapshot.

Full changelog:v1.0.2...v1.0.3

LevelCode v1.0.2

Choose a tag to compare

@github-actionsgithub-actions released this 26 Jul 02:02
b9c1289

LevelCode v1.0.2

A UI-polish release. The model picker is name-forward instead of a wall of detail, a finished plan folds away on its own, and the footer shed a redundant chip.

Highlights

A cleaner model picker

Picking a model used to mean reading three technical fields per row — the raw id (anthropic/claude-opus-5), the context size, and a 6.67× credits · 1000K ctx · ≈103 turns left second line. Handy for debugging, noise for choosing. Each model is now a single, name-forward line: just the name, an active check, and a lock on "coming soon" models — the way Cursor and Claude Code present them.

Your plan and remaining credits still sit in the picker's header, so the number that matters isn't lost; only the per-row debug detail is gone. Type to filter by model name.

A finished plan cleans up after itself

When the agent works from a checklist, a completed plan used to sit fully expanded in the sticky bar — a large part of the panel — long after the run had ended. Now, matching how Claude Code and Cursor handle a finished plan:

  • It auto-collapses to its green header the moment every item is done: still there, still one click to expand, just no longer hogging the view.
  • A dismiss (×) closes it outright — and also clears a plan left stranded by a run that was stopped or errored. It's keyboard-operable (Tab to it, Enter / Space to close), like the other controls in the plan bar.

A slimmer footer

Dropped the redundant home-icon "LevelCode Cloud" chip from the status bar. The mode-and-plan chip on the right (Gateway · Pro+, BYO key, Direct · no key) already tells you where your requests go.

Test coverage

  • 24 suites across the bundled extensions, all green on every release.
  • These are webview / picker UI changes, so they're verified by parsing the shipped chat.html — every script block, plus the webviewCss and creditFormat suites that read it — rather than by snapshot.

Full changelog:v1.0.1...v1.0.2

LevelCode v1.0.1

Choose a tag to compare

@github-actionsgithub-actions released this 26 Jul 00:26
a0850f4

LevelCode v1.0.1

A reliability patch. A transient upstream hiccup now retries and recovers instead of killing your run, and when something does fail you get an honest, readable message rather than a wall of proxy HTML mislabeled "OpenAI".

Highlights

Runs survive a transient upstream blip

When the model gateway briefly can't reach a healthy backend it returns a 502 / 503 / 504 — a momentary hiccup that used to end the whole run. LevelCode now retries once, before anything has streamed, so the common case (the backend is fine a second later) just recovers and your turn carries on. You'll see a brief upstream busy — retrying… instead of a dead run.

It's deliberate about when it retries: only a 502, 503, or 504, and only before any output has appeared — so a retry can never duplicate text or double-charge. It does not retry a rate-limit (429), a 500, any other 4xx, or an aborted request, and a 401 still refreshes your session as before. Hitting Stop during the wait stays instant.

Honest error messages

A gateway failure used to land in the chat verbatim, like this:

OpenAI API 502: <html><head><title>502 Bad Gateway</title></head><body>…

Two things were wrong: it was labelled OpenAI even for an Anthropic model on your LevelCode Cloud plan, and it pasted the raw nginx error page into the transcript. The same failure now reads:

LevelCode Cloud API 502: Bad Gateway

The route is named correctly — LevelCode Cloud, OpenRouter, or whichever provider actually handled it — and the body is parsed for a real message. An HTML proxy page carries none, so it falls back to the plain status reason instead of being dumped in.

Under the hood

  • The repository's LICENSE is now recognised as MIT by GitHub. The MIT text is kept verbatim so the detector matches it, the Code-OSS provenance + trademark notice moved to a dedicated NOTICE file, and the in-app license link points at a branch that exists (HEAD) rather than a main that never did (it had been a 404).

Test coverage

  • 24 suites across the bundled extensions, all green on every release.
  • providers.test.js grew to 28 cases: the error sanitiser (a real nginx 502 page collapses to Bad Gateway, a JSON {error:{message}} is preserved, the label names the route — not the adapter) and the retry (recovers on a 502-then-200, gives up cleanly after one try, never retries a 4xx or an abort). The retry is also exercised end-to-end against a stubbed stream, so the whole router → adapter path is covered, not just the helper.

Full changelog:v1.0.0...v1.0.1

LevelCode v1.0.0

Choose a tag to compare

@github-actionsgithub-actions released this 25 Jul 20:24
faa8eb5

LevelCode v1.0.0

LevelCode reaches 1.0 — running Anthropic's frontier model. Claude Opus 5 is now a first-class pick: in your roster if you're on a LevelCode Cloud plan, or in the OpenRouter list if you bring your own key — wired at its full 1M-token context and priced exactly where Opus 4.8 sat.

The 0.9 line built the parts — the agent loop, apply-then-review edits, the browser that opens itself, credits instead of dollars. 1.0 is that foundation, stable, running on Opus 5.

Highlights

Claude Opus 5

Opus 5 is Anthropic's newest frontier model, and LevelCode reaches it two ways:

  • On a LevelCode Cloud plan, it's already in your roster. Models come from the server, so Opus 5 shows up without updating the editor, metered in credits like everything else — roughly 195 Opus 5 turns on the top plan's 10,000 credits.
  • Bring your own OpenRouter key and it's in the picker.Claude Opus 5 — Anthropic · frontier, 1M · via OpenRouter, near the top of the OpenRouter list. Your key, billed by OpenRouter; the editor shows a cost estimate rather than a credit balance.

Two things it gets right that a bare model id wouldn't:

  • The full 1M-token window. LevelCode's context heuristic assumes 200K for any claude- model; left at that, the in-run context meter would warn "full" at a fifth of Opus 5's real budget. Opus 5 has an explicit 1,000,000-token caps row, so the meter reflects the window you actually have.
  • Honest pricing, whichever slug you type. Opus 5 costs what Opus 4.8 did — $5 / M in, $25 / M out, cached reads at $0.50 / M — so the OpenRouter "powerful" tier moved to the newer model for the same money, and prompt caching is on so repeated context bills at the cached rate. The fast variant is double ($10 / $50 per M); its price is pinned explicitly so the estimate can't quietly under-report it by half.

Your whole roster on the first open

Opening LevelCode with a briefly-expired Cloud token used to collapse the model picker to a two-model fallback — you'd see two models on a plan that offers the full lineup, and only a reopen fixed it. The roster fetch now refreshes the token and retries, and if anything still fails it keeps the last-known-good list rather than discarding it. You get your full roster on the first try.

Under the hood

  • 1M context, measured — not guessed. Opus 4.8 on OpenRouter is also a 1M-window route, so its caps row was split by route: the OpenRouter slug reports 1M, the bare Anthropic id stays at 200K. We deliberately did not raise the native path to 1M — this editor sends no anthropic-beta header there, so the long window isn't confirmed reachable, and promising room the API would refuse is the worse failure. Each route reports its own truth.
  • The "powerful" OpenRouter tier now names a model the registry knows. The old Opus 4.8 slug never was in it, so a per-node model override pointing there failed validation and fell back silently; moving the tier to Opus 5 fixes that as a side effect.
  • Release builds moved off the deprecated Node 20 runtime. GitHub had begun force-running the release actions on Node 24 with a per-step warning; the action pins now target Node 24 natively.

Test coverage

  • 24 suites across the bundled extensions, all green on every release.
  • catalog.test.js (13 cases) pins both sides of the 1M split — the OpenRouter slug at 1,000,000, the bare id at 200,000 — plus the heuristic default for an unlisted Claude model, so the context meter can't silently regress. sketch.test.js (16 cases) pins Opus 5's pricing, including the fast variant the family fallback would otherwise have billed at half.

Full changelog:v0.9.2...v1.0.0

LevelCode v0.9.2

Choose a tag to compare

@github-actionsgithub-actions released this 24 Jul 04:13
7fd4786

LevelCode v0.9.2

A small, practical release: your site opens itself in the editor while the agent builds it, and what a run costs is now measured in credits rather than dollars.

Highlights

The browser opens itself

LevelCode has always shipped a built-in browser — but you had to know the command to find it, so most people never did. Now it appears on its own: the moment the agent starts a web server in the background, the site opens beside the chat.

  • It updates as the agent works. Edits land on disk immediately (that's the apply-then-review model — Keep/Undo comes after), so your dev server's watcher fires HMR and the preview refreshes before you click Keep. Ask for a page, watch it appear.
  • It never steals your focus. A server coming up mid-run doesn't yank the caret away from whatever you're typing.
  • Closing it means closed. Each address opens at most once per session, so a chatty server can't reopen the tab you just dismissed, and a restart-on-save server can't stack one tab per reload.
  • Only local addresses, ever. The address is read from the dev server's own output — which is whatever a project's start script chose to print. So only localhost, 127.0.0.1, the IPv6 loopback [::1], and the bind addresses 0.0.0.0 / [::] (treated as localhost) are opened; a remote URL printed by a script is ignored. A hostile repo can't point your editor's browser somewhere else.

Turn it off with levelcode.ai.preview.autoOpen, or open previews yourself with Simple Browser: Show.

Credits, not dollars

The response bar under each run now reads Opus 4.8 · 46 credits · 1,279 left instead of dollar amounts. $1 = 100 credits, so a $100 Ultra plan is a 10,000-credit allowance.

This is a change of unit, not of price — nothing about what you pay or what a turn costs has moved. A balance that ticks down in dollars reads as money draining away; an allowance reads as something you're meant to spend, which is what it is.

  • Small runs stay honest. A cheap-model turn costs a fraction of a credit, so it shows as 0.4 rather than rounding to 0 and looking free.
  • One number everywhere. The editor and levelcode.ai/ai/account now format the same figure identically, down to digit grouping.

Under the hood

  • The dev-server address is sniffed from the accumulated output, not a single chunk. Node delivers stdout in arbitrary slices, so a URL routinely arrives split (http://local + host:5173/) and would otherwise never be recognised — the preview would silently never open.
  • verify.js gained its first test suite, and shed a stray NUL byte that had been making the whole file invisible to grep and diff.
  • Release builds no longer download a Playwright browser during bootstrap — an unnecessary network dependency that failed one architecture while the other passed.

Test coverage

  • 24 suites across the bundled extensions, all green on every release.
  • verify.test.js (16 cases) — the preview sniffers, led by the one that matters: a remote address in command output must open nothing. creditFormat.test.js (8 cases) extracts the real formatters out of the shipped chat.html, so the tests can't drift from what ships.

Full changelog:v0.9.1...v0.9.2

LevelCode v0.9.1

Choose a tag to compare

@github-actionsgithub-actions released this 23 Jul 22:21
2e1b857

LevelCode v0.9.1

Two big things this release: the agent gains external tools via MCP, and it learns to narrate its work instead of scrolling a wall of chips.

Highlights

The agent can use external tools — MCP support

LevelCode's agent now speaks the Model Context Protocol, so it can use tools from external MCP servers — a filesystem server, GitHub, Postgres, an internal company server — right alongside its own built-in tools. There's no glue code: an MCP tool becomes an agent tool directly.

Add a server in your user settings and its tools show up in the agent, namespaced server__tool:

"levelcode.ai.mcp.servers": {
"filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path"] }
}

Security is the feature here, not a footnote — an MCP server is a process LevelCode spawns with your privileges:

  • User-authored only. These live in your user settings; a repo's committed .vscode/settings.json can't add a server, so opening an untrusted project can never make LevelCode spawn a process. A repo's own .levelcode/mcp.json is read and listed but never started on its own.
  • Nothing runs unless you allow it. Every MCP tool call is gated by default — including under Autopilot. A tool runs only once you allow-list it in levelcode.ai.mcp.toolPolicy, and a server's own "destructive" hint can only ever tighten that, never loosen it.

This is the first slice: servers are configured in settings and tools are enabled through the allow-list. A per-call approval card and a one-click "Add MCP Server…" are what come next.

A calmer, narrated transcript

An agent run now reads like a colleague narrating their work rather than a flat scroll of chips and cards.

  • Short prose between actions. The agent says what it's about to do, then interprets what it found — in the same turn, so it never stops to chatter instead of working.
  • Activity folds into one card. Consecutive actions collapse into a single expandable group: while it runs, the header shows the live step; when it finishes, a past-tense summary takes its place — "Read and edited PLAN.md +56 −0, ran 2 commands."
  • Failures read as findings. A hiccup shows up as a one-line Correction: and a next step, not an alarm.
  • Plain-language command labels. A run_command shows what it does ("Run the extension unit tests"); the raw command stays tucked behind the card.
  • One question at a time.ask_user now asks a single question per prompt.

Smaller things

  • Shift+Cmd+I now focuses the chat — previously only Ctrl+Cmd+I did.
  • A single circle-check glyph wherever a "done" check appears, and an HTML5 shield icon for .html files.

Under the hood

  • MCP is three small, dependency-free modules — no SDK, in keeping with the plain-JS extension style. mcpProtocol.js (JSON-RPC 2.0 framing + typed-content flattening, pure), mcpConfig.js (server config, tool-name namespacing, and the approval policy — pure), and mcpClient.js (the stdio subprocess: spawned detached and group-killed on New Chat and reload, with a per-call timeout and an output cap so one server can neither hang the agent nor flood its context). The full plan and threat model are in docs/MCP.md.
  • The calm transcript ships as two halves — the voice lives in the agent's system prompt (agent.js), the grouping in the chat webview (chat.html). Scope and design are in docs/CALM-TRANSCRIPT.md.

Test coverage

  • 56 MCP unit tests, all off-editor (no process, no network): mcpConfig.test.js (41 — tool names stay provider-legal across a hostile corpus, a repo's config can never shadow or auto-start a server, untrusted keys are dropped before they can reach a prototype, and the allow-list never defaults to "allow") and mcpProtocol.test.js (15 — JSON-RPC framing with partial-line buffering, and base64 image/audio payloads never reaching the stored transcript).
  • The CI gate runs every bundled extension's suite on each release.

Full changelog:v0.9.0...v0.9.1

LevelCode v0.9.0

Choose a tag to compare

@github-actionsgithub-actions released this 21 Jul 18:45
c5e1f50

LevelCode v0.9.0

Two features this release: paste JSON and watch it tidy itself, and Kimi K3 arrives in LevelCode Cloud.

Highlights

JSON beautifies itself on paste

Working with minified JSON — one giant line, escaped, straight from a log or an API response? Paste it into LevelCode and it lands pretty-printed.

  • Any buffer. A .json file, a .txt, an untitled scratch tab — it doesn't matter what the editor thinks the buffer is. (That's the difference from the built-in format on paste, which only fires in a buffer already known to be JSON.)
  • Only when it's actually JSON. It transforms a paste only when the whole thing is a valid JSON object or array. A bare number, a string, code that merely contains JSON, JSON with trailing prose, or already-formatted JSON — all left exactly as pasted. Non-JSON pastes are never touched.
  • Yours to tune.levelcode.jsonPaste.enabled turns it off; levelcode.jsonPaste.indent follows your editor's indentation by default, or takes a number of spaces or "tab". And a normal paste is always one click away via the paste options.

It lives in the Notepad++ Pack, alongside the macros and line operations.

Kimi K3, in LevelCode Cloud

Moonshot's Kimi K3 — a 2.8-trillion-parameter model with a 1,048,576-token context window, built for long-horizon coding and agentic work — is now a selectable Pro model in LevelCode Cloud. Sign in, open the model picker, and choose it. No API key of your own required.

  • A choice, not a default. Your plan's flagship is unchanged. K3 sits in the picker with its cost shown honestly: it runs about 4× the credits per turn of the default (it reasons on every turn, and that reasoning is billed), so a Pro budget goes roughly a quarter as far on it. Pick it when the task is worth it.
  • The editor now meters it correctly. Select K3 and the context gauge reflects its real ~1M window instead of the old 200K default — so long sessions don't warn "full" five times too early.

Under the hood

  • extensions/levelcode-npp-pack/ — a pure analyzePaste() core (no editor dependency, fully unit-tested) behind a DocumentPasteEditProvider, so the paste transform is decided by tested logic and the size guard is measured in real UTF-8 bytes.
  • extensions/levelcode-ai/providers/catalog.js — a capabilities row for moonshotai/kimi-k3 (1,048,576 ctx) so the meter is right; the model itself becomes selectable via the LevelCode Cloud roster, no editor release required.
  • extensions/levelcode-ai/scripts/kimi-k3-spike.js — a standalone harness that drives the real agent tool-loop against K3, to confirm an always-on-reasoning model survives the round-trip before it's leaned on. See docs/KIMI-K3.md for the full scope.

Test coverage

  • test/jsonBeautify.test.js — 13 cases: minified→pretty round-trips, the container-only / trailing-junk / already-formatted guards, indentation, and a UTF-8-byte size cap that a multibyte payload can't slip past.
  • The pre-build CI gate now discovers every bundled extension's suite (not just one), so the new Notepad++-Pack tests run on each release.

Full changelog:v0.8.1...v0.9.0

LevelCode v0.8.1

Choose a tag to compare

@github-actionsgithub-actions released this 20 Jul 15:08
07b7341

LevelCode v0.8.1

The version LevelCode reports is now its own.

v0.8.0 turned on auto-update — and the first thing it revealed was that the editor had been describing itself with somebody else's version number. The update dialog read "Current Version: 1.126.0", dated months earlier, next to a commit that really was LevelCode's. Half upstream, half LevelCode, and confusing whichever half you trusted.

This is also the first release delivered by auto-update itself: if you are on v0.8.0, this one most likely installed on its own.

Highlights

The version you see is the version you have

1.126.0 is the Code-OSS base LevelCode is built on, and it has to stay 1.x — extensions declare compatibility against it (engines.vscode), so renaming it to 0.8.1 would reject every extension that requires ^1.x. The release version now travels alongside it instead:

  • Update tooltipCurrent Version: 0.8.1 (<commit>), with a Released date that reflects this release rather than the upstream base's build.
  • About — shows both, 0.8.1 — Code-OSS 1.126.0. About gets pasted into bug reports, where the base version is what explains extension-compatibility behaviour, so dropping it would lose real information.

Both values are stamped into the app at build time from the git tag.

If you are updating from v0.8.0: the update prompt you saw still said 1.126.0. That build predates the stamp and has no release version to read — the corrected display appears once v0.8.1 is running. Nothing went wrong.

Development builds can no longer impersonate a release

A build made from a commit after a release used to stamp that release's bare version — a local build five commits past v0.8.0 called itself 0.8.0. Off-tag builds are now labelled 0.8.0-5-g1a2b3c4, so a development build is visibly one. Builds cut at an exact tag are unaffected and stay clean.

Anything that isn't a recognisable version now fails the build outright rather than shipping a nonsense product version.

Under the hood

  • scripts/stamp-levelcode-version.mjsnew. Writes levelcodeVersion + levelcodeReleaseDate into the built app's product.json, leaving version untouched for extension compatibility. Refuses malformed input with a non-zero exit.
  • scripts/build-macos.sh — runs the stamp, tag-derived, after the existing strip steps. A checkout with no reachable tag skips it and falls back to the base version.
  • patches/levelcode-core.patch — 11 → 14 entries: optional product fields, the update tooltip, and the native About dialog.
  • docs/CORE-PATCHES.md — documents the three new core touches, and a regeneration hazard that silently fattened the patch by ~95 unrelated lines the one time it was hit.

Test coverage

The release gate now runs 14 suites, up from 13. It previously entered a single extension directory before collecting tests, so levelcode-updater's suite — including the check that the updater's Download button can never hand a user a raw .app.zip — was never executed in CI. It now discovers every extension, so a new suite is gated the moment it is added.

The gate also fails loudly if it matches zero test files. Reporting success while running nothing is the same class of bug as the one above.

Full changelog:v0.8.0...v0.8.1

LevelCode v0.8.0

Choose a tag to compare

@github-actionsgithub-actions released this 20 Jul 04:09
876afa9

LevelCode v0.8.0

The release that makes Check for Updates… real.

Until now that dialog always answered "There are currently no updates available." That was correct behaviour rather than a bug: the update feed deliberately refused to hand the built-in updater anything, because there was nothing it could safely install. Squirrel installs a signed .zip of the app, never a .dmg — and LevelCode only ever published dmgs.

v0.8.0 ships the missing artifact and the client-side plumbing around it. This is the first build published with update assets attached, so it is the release the update path starts from.

Highlights

Signed, notarized update assets

scripts/make-dmg.sh now emits LevelCode-<arch>.app.zip alongside the .dmgon the Developer-ID path only.

  • The dmg stays the human download (open, drag to Applications); the zip is update-only. They are not interchangeable.
  • Built with ditto -c -k --sequesterRsrc --keepParent, which preserves the stapled notarization ticket — so an app installed by an update still validates offline, with no Gatekeeper prompt. Re-zipping with zip(1) would drop it.
  • Emitted only when real Developer ID signing is used. Squirrel refuses an update whose signing identity doesn't match the running app, so an ad-hoc build can never masquerade as an update asset.
  • The zip is cut after notarization completes, never before — an unstapled app would otherwise ship.

The Download button can't hand you a raw zip

The notify-only updater's Download action preferred the feed's url. That was correct while url was a release page — but now that the feed serves the signed .app.zip there, the same code would have started a raw zip download instead of opening a page.

Download now resolves product.downloadUrl → this release's notes page → the feed base, with the install artifact dropped from the human path entirely. A future feed change cannot put a zip back behind that button.

Updates roll out behind two independent guards

The server only offers a build to the auto-installing updater when both hold: signed feed assets are declared live, and the resolved release actually has a signed zip for that architecture. Releases cut before this one therefore stay notify-only on their own, rather than relying on the rollout being performed in the right order. Architecture matching is by exact filename, so an Intel install can never be handed the arm64 build.

Under the hood

  • scripts/make-dmg.sh — emits the Squirrel update asset (plus a local-only .sha256 for hand-verifying a publish); header step list corrected to match the script, and the "unnotarized" warning scoped to the ad-hoc path it actually describes.
  • extensions/levelcode-updater/update.js — new pure downloadUrl(feed, product, base) helper.
  • extensions/levelcode-updater/extension.js — the Download action now delegates to it.
  • docs/AUTO-UPDATE.mdnew. The full feed contract, rollout order, risks, and the exit test, including which failure modes are enforced in code versus only documented.
  • docs/RELEASING.md — publishing steps updated for the second asset, and an explicit note that the .sha256 sidecars stay local (the feed reads GitHub's own asset digest).

Test coverage

  • extensions/levelcode-updater/test/update.test.js — 9 cases, 2 new, covering the Download path: the helper never returns feed.url even when that is a signed .app.zip, falls back to a page rather than an artifact, and tolerates a null feed or product. Both new cases were mutation-checked — restoring the previous preference order fails them.
  • The release CI gate continues to run the 13 levelcode-ai suites. Note the levelcode-updater tests are not part of that gate yet and are currently run manually.

Full changelog:v0.7.2...v0.8.0