Repository navigation
Releases: Sheetaldharshan200/Exasol-studio
Releases · Sheetaldharshan200/Exasol-studio
Release list
Exasol Studio v2026.10.0
Exasol Studio 2026.10.0 UDF editor: language-aware hints, context linting, AI ghost text. MongoDB as a data source; Virtual Schemas shelf; full ecosystem catalogue. GitHub: release reads fall back to github.com when the API allowance is spent; optional token to raise the limit; org-listing metadata (149 -> 4 requests).
Exasol Studio v2026.9.0
Visualizer, results and local-runtime work — release 2026.9.0 (#159)
* feat(studio): dashboards, folder data-loading, setup self-heal, and fixes
Dashboards
- Freeform AI-first dashboard system (features/dashboard): JSON-doc model,
canvas, widgets, cross-filter, drill-down, snapshot/export, live share.
- Interactive echarts: persistent instance + setOption (no re-init flicker),
category-axis fix, toolbox hidden while editing, report tooltips.
- Fixed-width dashboard; text widgets hug content; per-chart "Edit in notebook".
- Cross-filter only targets dimensions (never measures); animated filter bar.
Assistant / data loading
- Folders attach as ONE collapsible chip (composer + sent message); files keep
their subfolder path, saved without collision; import_csv accepts a saved
path (contained to the attachments dir) so a whole datasets/ tree loads,
subfolder -> schema, file -> table.
- App-control gate fixed: read config.settings().appControl (was always undef).
- Send is blocked with no connected model (no hang); model pill flags reconnect.
- Shield "Save" shows applying state and surfaces errors like the sandbox.
Local database setup
- Self-heal: a reused/adopted stale daemon that isn't query-ready triggers a
clean managed redeploy (force-stop + reclaim port), at SYS and MCP levels.
- Readiness patience is deadline-tuned: fast-fail an adopted daemon, wait out a
fresh cold boot.
- Fix stale arm64 executableSha256 pin for Exasol Personal v2.2.0 (verified
against the official archive).
Sidebar / Marketplace
- Dedup the managed-local "Exasol Personal (local)" so it shows once; card
connects when the DB is reachable.
- Marketplace catalog syncs in the background (6h poll) and raises an updates
badge; the open Updates tab refreshes and keeps last-known upstream tags.
Tests: agent-core 243, desktop 371 (added config, folder-attachment, updates,
attachment-routing coverage). tsc + cargo check clean.
* fix(marketplace,assistant): managed-component update on cards; unstick shield Save
- Managed components (e.g. MCP server) now show an available update ON their
Marketplace card using the SAME live upstream the Updates tab uses, with an
"Update to <ver>" button that routes to the Updates tab — no longer falsely
reads "Up to date" while the Updates tab has a newer version.
- Shield Save: stop gating the button on the applying state (a hung/pending
engine grant-sync left Save permanently disabled). Save is enabled whenever
there are changes again; the applying indicator is non-blocking with a 25s
safety timeout so it can never stick.
* fix(marketplace): stop periodic GitHub-API polling from exhausting the rate limit
The Updates tab and cards read "latest" from unauthenticated GitHub API calls
(marketRelease / componentsUpstream), capped at 60 requests/hour. The periodic
syncs I added re-fired those on 10m/5m timers, burning the quota to zero — after
which every release lookup fails and the Updates tab shows nothing (even for a
real update like MCP 2.2.0).
- Marketplace periodic sync now refreshes ONLY the rate-limit-free catalog.json
mirror + local installed/component reads — never the per-repo GitHub API.
- IndependentComponents periodic refresh re-reads the local installed list only;
upstream tags are fetched once on mount (+ manual Refresh), not on a timer.
- Live release fetches stay mount-only; the 6h background badge poll is unchanged.
* fix(marketplace): install-into targets are the LIVE connection list, no managed dupe
The Semantic Views "Install into" picker listed a hardcoded "Local database
(managed)" PLUS the managed local profile ("Exasol Personal (local)") — the same
database twice — and was fetched once on mount.
- Exclude the managed local DB from the profile list robustly (notes marker OR
name "Exasol Personal (local)" + local host + SYS user), so it appears only as
the single "Local database (managed)" entry.
- Read targets LIVE from the connection profiles: refresh on the picker opening
and on connection add/remove/reconfigure and DB-state events (event-driven,
no timer) — mirrors the Connections tab.
* fix(setup): recover a credential-desynced local DB via the master password
A reused/adopted local database whose SYS password was changed to the master
password (the unified-credential model) failed setup with 'authentication
failed' — the readiness probe only tried the stale deploy-time credential, and
the adopted self-heal redeployed without ever trying the master. Now the
adopted path tries the master-password recovery FIRST (cheap, no redeploy) and
falls back to it again after a redeploy, so a healthy-but-desynced DB reconciles
instead of failing.
* fix(assistant): Claude Pro/Max OAuth always shows the code-paste box
Selecting the Claude Pro/Max (subscription) auth method could hang on 'Waiting
for authorization…' with no way to enter the code: startOauth blocked in the
waiting state for 'auto' flows, and the paste box only rendered for method
'code'. Now the browser opens, state returns to idle immediately, any auto
callback is polled in the BACKGROUND, and the code-paste box is always offered
for an active flow — so the sign-in can always be completed.
* fix: keep model menu open during OAuth; tighten DB readiness waits
OAuth: opening the browser blurs the app window, which Radix treats as an
outside interaction and dismissed the model menu — taking the code-paste box
with it, so the code could never be entered. Keep the menu (and its connect
submenu) open whenever an OAuth flow is active: guard onOpenChange and the
content's onInteractOutside/onFocusOutside/onPointerDownOutside/onEscapeKeyDown.
DB setup: the redeploy readiness wait was 180s with 5s polls even though the DB
is ready ~15s after a restart — so a credential mismatch stalled for minutes.
Cut the redeploy deadline to 60s and the poll to 3s; on failure it already falls
through to the master-password recovery.
* fix(setup): use our own deployment directly — never adopt it via the registry
Setup 'adopted' Studio's own managed database through the shared registry,
whose stored credential drifts (stale sys password / old MCP identity). The
readiness probe then failed and every launch crawled through the stop/start
recovery ladder before coming up.
ensure_runtime now short-circuits: when Studio's own deployment exists, go
straight to ensure_personal — it reuses the running database with NO restart
and reads the credential from the deployment's own secrets.json (the on-disk
source of truth, kept in sync after every ALTER USER). Registry adoption
remains only for genuinely foreign local databases.
* feat(agent): master scenario skillset — route every intent to the right tool
Six builtin skills that make the agent act agentically instead of guessing:
- scenario-router: THE intent matrix (load/federate/schedule/dbt/ETL/visualize/
semantic/ML/export/catalog) with agentic ground rules — clarify once, plan,
act with tools, verify with real results, never dead-end.
- exasol-federation: multi-source questions — clarify sources, import+join on
Personal, virtual schemas on full Exasol, honest snapshot-vs-live wording.
- exasol-scheduling: dashboard auto-refresh vs exasol-scheduler daemon
(table-driven SQL jobs, AFTER chains, history table) vs launchd+exapump on
laptops (missed runs are not replayed by the daemon).
- exasol-dbt: official dbt-exasol adapter with the verified Personal profile
(encryption + validate_server_certificate: false), seed CSV pitfalls,
partition/distribute configs.
- exasol-etl-orchestration: stage -> transform -> schedule -> monitor composed
from the other skills, idempotent stages, real-count reporting.
- exasol-community-upgrade: the never-say-can't ladder — check Docker, install
Colima with permission, deploy Community Edition with permission, run
blocked capabilities (virtual schemas, kafka, BucketFS) there.
Facts grounded against exasol-labs repos, dbt-exasol README, and the Personal
Local capability matrix. skills.builtin.test.ts validates every builtin skill
parses, names are unique, the set is present, and load_skill references can't
point at phantom skills (it caught one). Seed flag bumped to v2 so existing
installs adopt the new defaults.
* feat(marketplace): Exasol Community database card + broader component catalog
Exasol Community (exasol/docker-db — full Exasol 8 with BucketFS, <=10 GiB) is
now a first-class Marketplace database with a real lifecycle:
- Rust community_db module: Docker checks (installed -> engine running ->
platform), LIVE version tags from Docker Hub (no GitHub rate limits; pure
version_tags filter/sort with unit tests), pull + run using the officially
documented flags (--privileged, --stop-timeout 120, data volume at /exa,
DB on 127.0.0.1:8574, BucketFS on 2581), start/stop/remove/destroy, streamed
progress over market:log, and automatic registration of the sys connection
(generalized profiles::ensure_local_profile).
- CommunityDbActions card UI: per-state actions (install Docker guidance ->
start engine -> version picker + Install & run -> running badge with
Connect/Stop/Remove), live boot log, honest Apple-Silicon emulation warning
(image is linux/amd64; upstream supports Docker on Linux).
- Catalog: exasol-community entry plus verified new components and drivers —
notebook-connector, dbt-exasol, exasol-scheduler, dash-server,
grafana-datasource, tableau-connector, terraform-provider-exasol,
exa-postgres-interface, exasol-mongodb-vs, more-functions; CI catalog
workflow items extended to carry their metadata.
- exasol-community-upgrade skill corrected to the verified reality: the Docker
path is exasol/docker-db via the new card; the Community Edition OVA is a
VirtualBox/VMware image (Intel-only on Mac).
Tests: rust 96 (3 new), desktop 371, agent-core 247 — all green.
* fix(marketplace): honest Community card, no more 'coming soon', do-it Docker setup, earlier connection save
- Catalog resolve precedence: explicit item name/description now OVERRIDE the
GitHub About line (exasol/docker-db's says 'Documentation for…', which
mislabeled a full database). The Community card reads 'Exasol Community —
full Exasol 8 database in Docker…'.
- Removed the derived 'coming soon' state entirely: reference items (scheduler,
grafana, tableau, terraform, postgres-interface, mongodb-vs, more-functions,
dash-server, drivers) now show their real 'Get' action opening the official
page instead of claiming they are unavailable.
- Docker prerequisites are now BUTTONS, not quotes: 'Install Docker' runs
brew install colima docker + colima start (headless, streamed log), 'Start
Docker' runs colima start or launches Docker Desktop and waits for the
engine — with the equivalent command shown for transparency.
- Personal bootstrap: save the built-in connection profile the moment the
database is query-ready — before the ExaPump/agent-skills downloads — so the
user can connect minutes earlier on a fresh setup; launches with a completed
setup keep the existing fast path (start DB only, verify in background).
* feat(marketplace): real installs for every driver/extension, honest updates, fetch-on-demand
Installs
- pickAsset extracted pure to marketplace/assets.ts (checksum/metadata filter,
os+arch matching, null when a platform-tagged release has no build for this
host) with 5 unit tests; scheduler/grafana/tableau/postgres-interface/
mongodb-vs flipped from reference to real binary installs
- driver-jdbc installs from Maven Central: tested <latest> parser, jar
downloaded into the managed folder, manifest records the REAL version
- binary items with no build for this host show 'No macOS build yet' + Get
link instead of an Install button that could only fail
Updates
- catalog.json semantics changed (PR #144): 'latest' is always the real
upstream release tag; the validated lock pin moved to a new 'verified'
field — the old design hid MCP Server 2.2.0 behind the 2.0.0 pin everywhere
- components_upstream falls back to the catalog mirror when the
unauthenticated GitHub API is rate-limited (pure mapper, tested)
- update state fetches ONLY at app open and on Updates/catalog tab clicks:
removed the Marketplace 10-min sync, the components 5-min interval, the
badge 6h loop and the Rust updates.rs 6h watcher
UI
- card actions pinned to the card bottom so buttons align across grid rows
- multi-select install: card checkboxes + floating 'Install selected' bar
(parallel queue, duplicate-proof via an active-installs ref, eligibility
re-checked at install time)
- multi-select update in Components: row checkboxes, Select all, sequential
'Update selected' (the DB engine update takes a maintenance lock)
Self-heal
- personal initial readiness deadline 150s -> 45s: the launcher already waits
for DB-ready, so the long patience only postponed the restart that heals a
wedged daemon resetting TLS handshakes ('Connection reset by peer')
CI
- refresh-runtime-components no longer stages the removed
exasol-semantic-views vendor dir (daily exit-128 failure since Sep 4);
Linux validation clones the repo fresh (PR #145)
- update-catalog workflow regenerates latest+verified and keeps the
open-PR-only guard from #142
* feat(marketplace): any-version installs everywhere + Studio & Exa engine update cards
Any version, live
- market_versions command lists real versions on demand: GitHub release tags
(binary items), PyPI releases (pip items — yanked/fileless versions
filtered), Maven Central (<version> list) — pure parsers/sorters tested
- every installable card gets a version dropdown (fetched on first open, never
on a timer); a chosen tag resolves THAT release's assets via
market_release(repo, tag) so the download matches the pick
- pip installs honor the chosen version (pkg==v), fall back to the verified
pin, else latest; dbt-exasol and notebook-connector finally get real
uv-pip installer arms (they previously fell through to the asset fallback)
- managed components on the Updates tab get the same dropdown (GitHub tags
for personal/exapump/exa-agent, PyPI for the MCP server) — update_component
installs the picked official version digest-verified
Studio + Exa engine
- StudioUpdateCard: Studio's own version card on the Updates tab — live check
against the signed release, Download (byte progress) → Install → Restart;
restart is safe because workspace state (tabs, connections, AI sessions)
persists on disk and is restored on relaunch
- the Exa AI engine (exa-agent) is now a listed managed component: installed
vs latest release of its real repo (Sheetaldharshan200/exa-engine — the
repo installs actually pull from; the display repo was mislabeled), updates
through the existing verify-or-refuse flow, and agent_restart bounces the
sidecar after an engine update so nothing keeps running on the old binary
(chat history is on disk and reloads)
CI
- refresh-runtime-components commit step gets the open-PR-only guard (the
merged-PR match silently skipped raising the validated refresh PR)
* fix(marketplace): Codex review findings on any-version installs
- verified pins are safe again: market_install_run gains an explicit
`requested` argument for the dropdown pick; the display version (catalog
latest) can no longer silently override the pinned mcp-server/pyexasol
pip specs on a plain Install
- update_component validates the version before it reaches a pip spec or a
GitHub release-tag URL segment (shared valid_version_tag gate)
- the install manifest records only what actually ran: installer-resolved
version, else the validated pick, else a validated display version — a
malformed request can no longer persist a version execution ignored
- PyPI lists skip versions whose every file is yanked, not just empty ones
- sort_versions_desc ranks pre-releases (1.0.dev1, 2.0-rc1) below their
final release; test added
- a finished install/update consumes its dropdown pick, so a stale hidden
pick can never redirect a later 'Update to latest' click
- exa-agent joins MIRROR_IDS + the catalog items list (PR #149), so the
rate-limit fallback also covers the engine
Marketplace.tsx remains >1000 lines (registry-driven card/update UI in one
place); versions.ts, assets.ts and StudioUpdateCard.tsx are extracted pure/
self-contained pieces, and IndependentComponents is the next extraction
candidate.
* fix(connections): deleting a connection actually removes it, and the row shows both actions
Removal
- deleting a profile now also unpublishes it from the shared ~/.exasol
registry (entry + OS-keychain secret + legacy 0600 credential file) - every
list re-imports registry entries that are "missing locally", so a delete
that only touched Studio own store was resurrected on the very next refresh
- guarded for shared ids: two local profiles can derive the same registry id
(the shared id ignores driver and case), so unpublish is skipped while any
surviving profile still uses it
- the registry rewrite now carries the CLI defaultId through (previously the
field was silently dropped on every read-merge-write) and clears it only
when the removed entry WAS the default; pure without() core + tests
Sidebar
- saved-connection rows no longer swap Connect for the bin on hover: the
wrapper carries the highlight, the connect pill stays visible (and lights
up), and the trash fades into its own always-reserved slot - both actions
side by side, keyboard-focusable, no overlay or layout shift
* fix(connections): self-heal the managed profile, prune internal AI-identity duplicates
The user removed a connection and ended up with the managed "Exasol Personal
(local)" profile gone (nothing recreated it while the DB was running) and
three identical internal "Local Exasol (AI read-only)" rows (stale MCP
identities, resurrected as user profiles by the shared-registry import).
- every profile listing now reconciles the managed local connection: while
the managed database is installed, a missing profile is recreated from the
deployment secrets (write-free otherwise) and the bootstrap status is
repointed at the NEW id so the permanent local card stays wired
- internal STUDIO_MCP_* identities are not user connections: stale ones
(not referenced by agent/mcp-identity.json) are pruned with their registry
entries and secrets; publish skips them and cleans old registry copies;
import refuses them; save_profile never publishes them, not even
transiently on create
- prune is conservative: no marker or an unreadable one means UNKNOWN (prune
nothing), and identities created in the last 10 minutes are never pruned —
provisioning saves the profile before it rewrites the marker
- removing the managed row now says honestly that it will be re-created
while the local database is installed (matched by notes marker OR managed
name, since the backend recreates by endpoint)
* feat(marketplace): version lists for every installable item, including the in-Studio drivers
The drivers that run inside Studio (pyexasol, SQLAlchemy, JDBC, ODBC) took
the driver-runtime card branch, which never rendered the version dropdown —
exactly the cards where the user looked for versions first.
- the version picker is extracted and shared: it now shows on the
runs-inside-Studio driver cards too; picking a version offers a
"Download {v}" action (managed marketplace folder) while "Install & use
here" keeps setting up the verified pinned runtime the SQL editor uses
- AI Lab lists Docker Hub tags (it is an image, not a pip package) and pulls
the picked tag; its permission plan now tells the truth (docker pull)
- new "dockerhub" version source with bounded pagination (tags past page one
no longer vanish) and a schema-surprise error instead of a fake empty list
- GitHub version lists are disk-cached for 1h under a process-wide lock with
temp-file+rename writes (no lost entries or torn reads on concurrent
dropdowns); stale cache is served over a rate-limit error; future cache
timestamps are not treated as fresh
- a failed version fetch renders "could not load - reopen to retry" instead
of the misleading "No published versions found"
- AI Lab tags validated against Docker tag grammar before reaching the CLI
* feat(marketplace): every driver installs from its native source - none are links anymore
All drivers are now independently installable, unpinned and unmanaged by
Studio, at any version, from the registry that actually publishes them:
- driver-ts: npm (@exasol/exasol-driver-ts) package tarball
- driver-go: Go module proxy zip
- exarrow-rs: crates.io crate
- driver-r: GitHub tag source package (repo exasol/r-exasol wired up)
- driver-odbc + driver-adonet: the OFFICIAL Exasol downloads portal via its
machine-readable index (x-up.s3.amazonaws.com packages.json) - per-platform
version lists (macOS universal ODBC included), downloads verified against
the portal sha256 (verify-or-refuse); ADO.NET resolves its Windows package
from any host, labeled honestly
- driver-websocket: current spec snapshot (protocol description + client
implementations; a living spec with no releases, so no version list)
- new market_versions sources: npm / goproxy / crates / exasol-downloads;
pure portal_artifacts platform mapper with tests
- independent-but-usable: "Use {v} in Studio" on the JDBC card points the SQL
editor at a downloaded jar (same override as Drivers > Use custom JAR)
Codex review fixes:
- valid_version_tag rejects "." / ".." / leading-dot values (URL dot-segment
normalization could have escaped the intended registry endpoint)
- uninstalling driver-jdbc clears a JDBC override that points into the
deleted folder, so the Drivers UI never shows a jar that no longer exists
* feat(marketplace): one seamless install button, zero Get-only items left
One button
- the runs-inside-Studio driver cards collapse Download / Install / Use into
ONE action: picking a version makes the same "Install {v} & use here"
button download that version, wire it in where supported (the JDBC jar
becomes the SQL editor driver) and set up the runtime
- Codex findings applied: a failed download keeps the pick, notifies and
STOPS (never runs runtime setup as if it worked); the version menu freezes
during a direct driver flow; a synchronous flow guard covers the
runtime-only path against fast double-clicks
Everything installable
- terraform-provider: real binary install (its releases ship darwin/linux/
windows zips - pickAsset routes the right one)
- dash-server: pip-installed from its GitHub tag tarball into a managed
environment (not on PyPI), with a version dropdown
- more-functions: SQL function library snapshot download, ready to run in
the SQL editor
- no catalog item is a Get-only link anymore; "Get" survives only as the
no-build-for-this-platform fallback
* fix(marketplace): driver button just says Install
* feat(marketplace): zero-prerequisite installs, live driver-dropdown sync, honest installed cards
One install = usable, no dependencies
- every downloaded archive is extracted immediately (tar.gz/tgz/zip/crate;
.taco/.jar/.whl/.nupkg stay whole for their tools), and extracted CLI
binaries are linked onto Studio PATH (terminal + AI agent)
- extraction and linking are hardened per Codex review: symlink entries are
never followed or linked (symlink_metadata walk + canonical containment
under the unpack dir), a denylist keeps extracted binaries from shadowing
common tools (git/docker/python/...), and a real managed binary in the bin
dir is never replaced
- ODBC is one click end to end: portal download, checksum verified BEFORE
extraction (nothing derived from an unverified file, not even a symlink),
library wired straight into the pyodbc bridge by PATH (no OS registration;
braces in the path escaped per ODBC grammar), runtime venv ensured;
override-write failures now propagate instead of claiming success
- AI Lab provisions its own container engine via the shared
ensure_engine_ready (start a stopped engine, or install Colima headlessly)
instead of dead-ending on "install Docker first"; runs via spawn_blocking
so minutes of brew/colima work never occupy async workers; engine_install
gains the same 90s readiness wait as engine_start (delegation regression)
Synced UI
- the connection form driver dropdown and the Drivers table now refresh on
market:done and studio:drivers-changed — an installed driver appears
immediately, no remount needed
- installed cards keep the version dropdown and offer "Switch to {v}"; the
"Up to date" pill only appears when a known latest CONFIRMS it, otherwise
the card says "Installed · {version}" (repo-less portal drivers showed a
false "Up to date" with no version at all)
* feat(marketplace): one card system - managed components live on their cards, no separate panel
- the "Components" panel on the Updates tab is gone (330 lines deleted):
managed components (Personal, ExaPump, MCP Server, Exa Agent, Semantic
Views) show installed state straight from list_components on their own
cards and update IN PLACE via switchManaged (verify-or-refuse
update_component; the DB engine backs up first; the AI engine restarts its
sidecar after a switch)
- the Updates tab is now Studio own card + the same catalog cards filtered
to those with an available update; the badge count computes directly from
components + live upstream
- managed cards join multi-select: installs and updates are one gesture
("Install / update selected" - managed updates run sequentially under the
DB maintenance lock, addon installs stay parallel)
- Exa Agent gets its own catalog card (bundled engine, versions from its
release repo); Uninstall hides on managed cards (they have their own
lifecycle - a marketplace-folder delete would be meaningless)
- Exasol Personal card: any-version switch via the managed path + a Back up
button (both used to live on the removed panel)
- JDBC card gains "Custom JAR" (same override as the Drivers tab)
- the connection page Drivers tab reaches marketplace parity: per-driver
any-version dropdown (same sources), picked versions install through the
marketplace path first (the picked JDBC jar is wired in), then the runtime
is ensured
- marketplace open no longer freezes: market_detect and community_status
were SYNC Tauri commands running subprocess probes on the main thread -
both now run via spawn_blocking; tab switches fade in
* feat(marketplace): industry-standard rate-limit handling + every card in the batch
Version lists never dead-end on the rate limit
- ETag conditional requests: refreshes send If-None-Match, and GitHub 304
answers do NOT count against the unauthenticated 60/hr limit - once a list
has been fetched once, keeping it fresh is free
- graceful ladder when the API is unavailable: fresh cache (1h) -> free ETag
revalidation -> stale cache over any error -> the release cache known
latest tag as a one-entry list (the dropdown still offers the newest
version) -> only then the error message
Multi-select covers every installable card
- the runs-inside-Studio drivers (pyexasol, SQLAlchemy, JDBC, ODBC) were the
only cards without a checkbox: they now join the batch, and their batch
action is the full seamless runtime setup (skipped when already ready);
addon installs stay parallel, managed updates stay sequential
* fix(marketplace): tab switches render smoothly
Switching Kits -> Catalog mounted ~28 cards in one synchronous render — made
WORSE by the key={nav} fade wrapper, which force-remounted the whole subtree
on every switch. Now:
- nav changes run in a React transition: the clicked tab highlights
immediately and the heavy grid render is time-sliced (content dims slightly
while pending)
- the fade runs imperatively on the ref — no remounts
* fix(docs): no white flash opening a guide
The docs open in an iframe, and an iframe paints WHITE until the page inside
applies its CSS — a hard flash in dark mode when clicking a guide from the
home page. The frame now stays invisible until onLoad (themed loader shown
over bg-editor), then fades in; a new URL (path change, theme toggle) hides
the fresh frame again until its own load fires.
* fix(marketplace): progressive card reveal - no tab-switch commit is big enough to hang
React transitions time-slice renders but the COMMIT of ~29 cards (each with
Radix dropdown triggers) was still one block, so Kits -> Catalog and every
other horizontal tab switch stalled. Cards now mount 12 at a time (first
chunk instantly, the rest 40ms apart), across the grouped catalog sections
and the flat tab grids alike.
* feat(ui): results tabs use the underline selection language; editor font is selectable
- the Results / Query Performance tabs drop the colored pill background for
the same underline selection the Marketplace tabs use
- Settings > SQL Editor gains an "Editor font" selection (JetBrains Mono,
SF Mono, Menlo, Monaco, Fira Code, Cascadia Code, Source Code Pro, IBM
Plex Mono, Courier New) wired into Monaco with a clean fallback stack;
applies live via the settings:changed broadcast — previously the family
was hardcoded so no font selection could work
* fix(ui): brand loader during marketplace tab switches; settings sync hardened both ways
- while a tab transition is pending the THEMED brand loader overlays the
dimmed content — the screen never looks frozen even on a slow mount
- settings sync can no longer go stale in either direction: the main window
re-reads and re-applies settings whenever it regains focus (belt and
suspenders against a missed settings:changed event), and the Settings
window merges external changes live instead of holding its mount-time
snapshot
* fix(marketplace): version lists work on rate-limited networks via releases.atom
On shared/corporate IPs the unauthenticated GitHub API 403s PERMANENTLY
(60/hr per IP, shared by everyone behind the NAT) — Exasol Personal always
showed "Couldn't load the version list" because no fetch ever succeeded
(no cache to serve, and its release-cache entry was null).
The releases.atom feed is a plain web endpoint with NO API rate limit: when
the API fails, tags now come live from the atom feed (parsed from entry ids
by a pure, tested function; deduped, validated) and are cached like an API
answer. Ladder: fresh cache -> API+ETag -> atom feed -> stale cache ->
release-cache latest -> error.
* docs: agentic architecture spec - honest gap analysis + phased plan
Maps the User -> Router -> Planner -> {Metadata, Semantic, Tools, DAG,
Policy, Verification, Observability, Evaluation} -> Answer reference
architecture against the real code, layer by layer, and specifies five
phases with designs, steps, Exasol assets and acceptance criteria:
P1 automated verification, P2 explicit plan object with preview,
P3 run observability (traces/tokens/latency + DB profile links),
P4 evaluation suites (seeded Nano in CI, results stored in Exasol),
P5 execution DAG (client core + in-DB offload via DISTRIBUTE BY /
Lua execute scripts / scheduler chains).
* feat(agent): P1 automated answer verification (agentic spec)
Every SQL-backed answer can now carry an independently reproduced stamp:
verified / mismatch / unverified — never silently trusted.
- verify.ts (pure, 7 tests): plans the LAST result-bearing read of a turn,
refuses non-deterministic SQL, downgrades truncated results to row-count
compare; value compare is order-insensitive with numeric tolerance
(DECIMAL-as-string handled; null never equals 0 or empty string)
- db.verifyQuery: a DEDICATED throwaway session — reproduction is genuinely
independent of the session the answer ran on
- desktop panel path (engine -> MCP gateway): run_query gains verify:true;
the gateway reproduces the result (5s budget) and stamps the response;
the tool description + scenario-router skill instruct the model to verify
the answer-backing query only and to QUOTE the stamp - on mismatch both
numbers are presented, never hidden. External MCP clients get this free.
- agent-core loop path: per-turn sqlRuns tracking, verification after the
final message with a typed "verification" event + audit record; honest
unverified outcomes on timeout/failure/nothing-verifiable
- frontend event union mirrors the new event
Spec: docs/agentic-architecture-spec.md P1.
* feat(agent): P2 explicit plans + P1 verification hardening (Codex findings)
P2 — explicit plan object (agentic spec)
- plan.ts (pure, 6 tests): schema validation (unique ids, known deps, no
cycles, 12-step cap), computed write-approval requirement (write SQL via
classifySql or write-shaped tools), enforced status transitions (a step
cannot start before its dependsOn are done; unapproved write plans cannot
start write steps; failed -> running is the explicit retry), progress
- plans-store.ts: one JSON per plan + a current pointer under dataDir/plans
- gateway: POST /gateway/plan | /plan/approve | /plan/step, GET /plan/current;
MCP tools propose_plan / approve_plan / update_plan_step with descriptions
that force the user-go-ahead gate for write plans (never self-approve)
- live plan card in the panel: plan updates push through the action queue
server-side (informational — bypasses the app-control gate by design,
and the bridge now always polls; CONTROL actions stay gated at enqueue);
PlanCard renders goal, per-step status, awaiting-go-ahead badge
- dependsOn recorded from day one: it is the execution seam P5 runs on
- scenario-router: multi-step work proposes a plan first; single-step
questions get zero ceremony
P1 hardening (Codex review findings applied)
- comparison is now lossless and type-honest: DECIMAL strings canonicalize
textually (no float round-trip, safe beyond 2^53), VARCHAR "1" never
verifies equal to numeric 1 (type drift IS a mismatch), JSON row
serialization kills delimiter/sentinel collisions; test added
- db.verifyQuery self-limits: the 5s timeout CLOSES the throwaway session,
so a slow verification never keeps running in the background (loop and
gateway callers dropped their non-cancelling races)
- non-determinism denylist extended (GETDATE, UUID, SYS_GUID, ROWNUM, ROWID,
NEXTVAL, CURRENT_USER/SESSION_USER/CURRENT_SCHEMA, SCOPE_USER)
- session.sqlRuns bounded (last 20 runs); run_sql_batch reads now feed
verification too; every verification outcome is recorded (not just some)
and survives session reload via a replay() case; planVerification runs
inside the guard so it can never break finalize
* feat(agent): P3 run observability - trace spans, JSONL store, usage panel
Spec phase P3 (docs/agentic-architecture-spec.md): every turn, tool call and
gateway query now lands as a span in a local trace store, surfaced in a new
Settings > AI > Agent usage panel.
- trace.ts (pure, tested): TraceSpan, ToolSpanPairer (start/end by callId,
200-cap, no fabricated durations), summarizeSpans (turns/tools/gateway,
failures, tokens by provider and by day, avg tool latency)
- trace-store.ts (tested): daily JSONL under dataDir/traces, calendar-day
30-day prune, bounded reads (20k spans), torn-line tolerant
- session.ts: static traceSink + per-session pairer hooked into emit();
provisional streaming tool-starts are excluded from pairing so a
pre-id chunk can never leak an open span
- loop.ts: turn span in finalize (tokens, provider, duration, steps) AND
in the error/abort path (ok:false) so failed turns stay visible
- server.ts: TraceStore + sink wiring, gateway span on run_query for both
success and failure, GET /v1/traces/summary + /v1/traces/recent
- desktop: agent.tracesSummary/tracesRecent + AgentUsage settings panel
(day-window picker, provider table, recent activity; stale-response
guarded refresh)
* feat(agent): P4 evaluation layer - golden suites, scoring core, STUDIO_EVALS
Spec phase P4 (docs/agentic-architecture-spec.md): a third eval tier that
measures ANSWER quality on a real database, beside the existing
deterministic (evals/run.ts) and live DB-free (evals/live.ts) tiers.
- src/evals.ts (pure, 18 tests): suite parsing with strict validation
(empty values/patterns are rejected - they would match everything),
numeric answer matching (separator-normalized on both sides, relative
tolerance, adjacent numbers never merge), and per-kind scoring
(value / rowcount / sql-contains / tool-used / refusal). Refusal
distinguishes gate-held (write attempted, permission denied) from
write-actually-ran - tool-start fires before the permission ask.
- evals/suites/core.eval.json: 10 seed cases over a self-contained
EVAL_FIXTURE schema (metadata, describe, counts, aggregation, group-by,
join, filter, rowcount, refusal, tool-check).
- evals/golden.ts (pnpm evals:golden): discovers local Exasol Personal
deployments (or EXA_EVAL_DSN, redacted in errors), runs each question
through the real turn loop with permission asks denied, scores from the
turn evidence, records runs into STUDIO_EVALS.RUNS/RESULTS (escaped
literals), exits non-zero below --min-pass (default 0.8). Provider
registry reads the REAL config (keys/base URLs) while runTurn keeps a
throwaway config so provider-limit writes never touch the user's.
- Agent evals System dashboard (system-dashboards.ts): pass-rate trend,
failure counts by case, recent runs, latest failures.
- ci.yml: the deterministic tier now gates every PR.
Validated end to end against a local Personal DB: two runs recorded, the
threshold gate fires, and the refusal case passes via the held gate.
* feat(agent): P5 execution DAG - parallel plan steps, retry, compensation
Spec phase P5 (docs/agentic-architecture-spec.md), the last layer: an
approved plan's SQL steps now execute as a dependency DAG instead of
one-by-one.
- dag.ts (pure, tested): nextActions() decides everything from durable
plan state - ready set from dependsOn, bounded concurrency (budget order:
compensations, retries, fresh runs), exponential backoff from persisted
attempts, skip cascade past hard failures, onFailure compensation
triggering (comps also retry, and count against the budget), approval
block, finished detection; normalizeAfterCrash() fails interrupted steps
so resume just recomputes the ready set.
- plan.ts: PlanStep gains onFailure (validated: exists, not self, nothing
may depend on a compensation step, comps have no deps and no own hook)
and attempts (consumed on every running transition - durable, so retry
budgets survive a crash); planProgress ignores untriggered comps.
- dag-executor.ts (tested against a scriptable stub DB): applies the
actions, runs reads via db.query / writes via db.execute, persists
through save() after every transition (best-effort - a failed disk write
never re-classifies SQL that already ran), re-reads step state after
backoff sleeps, refuses re-entry on an already-executing plan, refuses
sql-less unfinished steps (pending compensations included) and
multi-statement steps, and breaks instead of spinning on stalls.
- Gateway: POST /gateway/plan/execute (exposure/caps checks, the same
writePolicy=deny guardrail run_sql honors - plan approval authorizes
writes only under policy ask, plus a gateway trace span) and the
execute_plan MCP tool; propose_plan schema gains onFailure.
- PlanCard: N-in-parallel badge, per-step after-deps hints, dimmed
on-failure compensation steps; progress counting matches planProgress.
- scenario-router skill: when steps can fan out INSIDE the database,
prefer DISTRIBUTE BY + SET UDFs / Lua pquery / scheduler AFTER chains -
the SQL is the offload, the executor runs it as one step.
27 dedicated tests (12 DAG, 9 executor, 6 plan); 307 agent-core total.
* fix(agent): DAG steps run on isolated DB sessions - shared websocket hangs
Live acceptance smoke caught what the stub DB could not: parallel plan
steps were sharing the ONE pooled websocket driver, and the Exasol
protocol is strictly request-response per session - two concurrent
statements interleave frames and hang the executor forever.
- db.ts: queryIsolated/executeIsolated - one statement on its own
throwaway session, closed in finally (the verifyQuery pattern).
- server.ts: /gateway/plan/execute passes the isolated pair as the
executor's StepDb, so DAG parallelism maps to real parallel sessions.
Verified live against a local Personal DB: 6-step plan with two parallel
branches + join completed, the doomed branch retried once (attempts=2),
failed hard, ran its onFailure compensation, and the run reported
ok=false honestly - 1.1s end to end.
* docs(wiki): P3-P5 review findings - shared-websocket hang, refusal semantics, comp-step rules
* test(agent): end-to-end integration tier - the whole P1-P5 stack on the real gateway
pnpm smoke:gateway boots the REAL agent-core server in-process (temp data
dir, random port, bearer auth) and drives every layer through the actual
HTTP gateway the desktop app and MCP clients use - 16 checks, no model
needed, everything deterministic plumbing:
- P1: verify:true reproduces a result on an independent session; RANDOM()
is honestly 'unverified'
- P2: a write plan requires approval and execution is BLOCKED before it
- P5: a diamond DAG completes both branches + join, the doomed branch
retries once then fails hard, its onFailure compensation runs, the run
reports ok=false honestly, and re-executing a settled plan is a clean
no-op (resume semantics)
- Guardrail: writePolicy=deny refuses even an approved plan (asserted
against an EXPLICIT plan id - the routes' current-plan fallback could
otherwise false-pass), restored in finally
- P3: the activity lands as gateway spans in /v1/traces summary + recent
Exit paths are safe: a mid-run crash drops the smoke schemas over a
direct isolated session and removes the temp dir (it holds the gateway
token while alive). Local-DB discovery + DSN parsing moved to
evals/local-db.ts, shared with evals/golden.ts (behavior identical,
re-probed). Spec header now carries the shipped status, the per-layer
state table, and the five-tier verification matrix; graphify graph
refreshed for the P3-P5 modules.
* feat(agent): self-maintaining semantic layer - auto validate + surface refresh
The Semantic Views layer now keeps itself current: EVERY database change,
from EVERY surface, triggers a debounced semantic pass on that connection.
- semantic.ts (pure, tested): classifySemanticImpact - schema DDL
(CREATE/ALTER/DROP/RENAME) vs data loads (INSERT/UPDATE/DELETE/MERGE/
IMPORT/TRUNCATE) vs none; leading comments stripped so a commented
migration still classifies; EXECUTE SCRIPT never self-triggers.
- semantic-sync.ts (tested with a scriptable framework stub): per-
connection debounce (an import burst = ONE pass), serialized passes,
silent no-op without the framework. A pass runs
SEMANTIC_ADMIN.VALIDATE_MODEL for every active model and - for SCHEMA
impact only - SEMANTIC_ADMIN.REFRESH_SEMANTIC_SURFACE for models that
are ALREADY PUBLISHED and validated (REFRESH wraps PUBLISH_MODEL;
drafts are never auto-published). Issues come from
SEMANTIC_CATALOG.CURRENT_VALIDATION_ISSUES, errors ranked first.
- db.ts onWrite: the single choke point - fired after every SUCCESSFUL
write in execute/executeIsolated, and for bulkLoad only AFTER COMMIT
(observers never see statements a rollback can retract).
- server.ts: wires the hook; connect-time VALIDATE-ONLY pass catches
changes made outside the agent (exapump, other clients); results land
as a trace span + the same studio:notice toast the Rust path shows.
- tools.ts: semantic_models tool (models + status + published schema +
current issues) - the spec paragraph-6 discovery tool; scenario-router
now says to call it first and prefer published measures over ad-hoc SQL.
- Rust semantic_sync.rs/query.rs: Studio-editor statements get the same
treatment - schema changes validate AND refresh published surfaces,
bulk loads (IMPORT/MERGE/TRUNCATE) validate only.
Codex findings fixed pre-commit: pre-commit bulkLoad notifications,
Rust refreshing surfaces on data loads, comment-blind classification,
and republish-on-every-connect churn.
* feat(agent): semantic coverage for new datasets - detect, notify, draft
Loading a NEW dataset previously left it semantically invisible: the sync
validated existing models but nothing said 'this schema has no model at
all'. Now it does, and drafting one is a governed one-ask flow.
- semantic.ts: uncoveredSchemas (pure, tested) - user schemas with tables
that no ACTIVE model binds; system/framework/fixture schemas excluded by
literal name (published schemas excluded dynamically by NAME from
SYS_SEMANTIC.MODELS, never by prefix guess); catalog casing preserved
for quoted identifiers, matching case-insensitive. classifySemanticImpact:
EXECUTE SCRIPT is now schema-impacting (Lua pquery can run DDL - the
in-DB offload path - and drafting a model SHOULD trigger a validation
pass); only the sync's own VALIDATE_MODEL/REFRESH_SEMANTIC_SURFACE calls
are exempt, so it can never self-trigger.
- semantic-sync.ts: every pass reports uncoveredSchemas; trace meta and a
new info toast ('Dataset X without a semantic model - ask the assistant
to draft one').
- tools.ts: semantic_models returns schemasWithoutModel; NEW semantic_admin
tool (one SEMANTIC_ADMIN script by name via CALL_ADMIN_JSON - an AUDITED
read-only allowlist runs freely, everything else asks the user with the
FULL untruncated payload) and semantic_apply_definition (dry-run default,
commit asks with the full definition). Drafts stay DRAFT - publishing
needs the user's explicit ask.
- dag-executor.ts: EXECUTE SCRIPT steps route through the query path - the
driver's execute() throws on a script's result table AFTER its side
effects committed, which double-ran scripts on retry (found live:
duplicate model). Gateway wiring notifies the semantic sync for script
steps.
- scenario-router: the new-dataset drafting discipline.
Codex findings fixed pre-commit: truncated permission previews, the
write-observer gap for script steps, over-broad EXECUTE routing, the
prefix-based allowlist, uppercasing away quoted identifiers, and prefix
exclusions hiding real user datasets.
Live-verified twice against Exasol + Semantic Views 0.2 (8/8): load
dataset -> reported uncovered -> draft via approved plan (CREATE_MODEL +
ADD_ENTITY through CALL_ADMIN_JSON) -> stays DRAFT -> next pass validates
the new model and coverage closes.
* feat(agent): semantic-layer parity on the gateway surface
The visible AI panel (MCP gateway) previously had NO semantic tools - the
loop/CLI surface got them all. Both surfaces now share one implementation:
- semantic.ts: SEMANTIC_READONLY_SCRIPTS - the single audited allowlist
both surfaces gate on. VALIDATE_MODEL is deliberately excluded: it
records validation runs (a catalog write) and the automatic sync is the
validation authority; manual validation goes through the approval paths.
- semantic-sync.ts: semanticOverview(query, id) - one snapshot (models,
open issues, schemasWithoutModel) shared by the loop's semantic_models
tool (refactored, duplicated SQL removed) and the new gateway route.
- server.ts: resolveGatewayDb (name/id + exposure + SQL-cap gates);
GET /gateway/semantic/models; POST /gateway/semantic runs ONE read-only
SEMANTIC_ADMIN script via CALL_ADMIN_JSON on an isolated session -
catalog mutations get a 403 pointing at the approved-plan path, so the
gateway's write gate stays singular.
- mcp-gateway.ts: semantic_models + semantic_call tools with the
discovery-first / draft-via-approved-plan discipline.
- loop.ts: progressive disclosure now surfaces ALL semantic tools when the
layer is ready (discovery and authoring included), not just the compilers.
- evals/golden.ts: when the target DB has the framework, the runner writes
the same capability marker the desktop uses, unlocking loop semantic
tools; suite cases prefixed 'semantic-' are SKIPPED (exit 0 with an
honest message) where the framework is absent. New 'semantic-first'
golden case: the spec's 'used the semantic layer when available'
expectation.
Codex findings fixed pre-commit: VALIDATE_MODEL on the no-approval
conduit, semantic_models missing from progressive disclosure, and the
confusing 'no eval cases found' when a semantic case is skipped.
Live-verified 5/5 against Exasol + Semantic Views 0.2 (models snapshot,
coverage, COMPILE_SQL conduit, mutation refusal, name validation); the
16-check integration smoke stays green.
* docs(wiki): semantic auto-sync design rules and live-run findings
* fix(market): Semantic Views install survives the GitHub API rate limit
The Marketplace install of Semantic Views (and the agent-skills source
fetch - both callers of upstream::fetch_source_tree) failed with 403 on
shared corporate NATs: the source tarball came exclusively from
api.github.com, whose unauthenticated per-IP limit is permanently
exhausted there. Same root cause as the version-list failures fixed
earlier; same remedy:
- try the API tarball first (exact behavior as before when it works)
- on failure, resolve the default branch's commit sha from the repo's
commits/HEAD.atom feed (not rate-limited), then download from
codeload.github.com/<repo>/tar.gz/<sha> (also not rate-limited)
- revisions normalized to the 7-char short sha so both paths record the
same value for the same commit (readiness markers must not churn)
- both fetch errors carried in the failure message when everything is down
sha_from_commits_atom is pure and unit-tested; verified live: the atom
feed resolves real shas for exasol-labs/exasol-semantic-views from this
network, where the API tarball returns 403.
* fix(market): Semantic Views readiness probe is Studio's own, not upstream's
The install got past the fetch (previous fix) and then failed with
'readiness probe exited with code 2': Studio ran tools/probe_ready.py
from the fetched source tree, and upstream no longer ships that file -
python exits 2 on a missing script, so every install failed AFTER the
framework was already installed.
The probe is Studio's contract, so it now lives IN Studio as an embedded
script (the probe_credentials precedent in this file), run via python -c:
exit 0 = framework + complete example, 1 = framework missing, 3 =
framework only (clean), 4 = SALES/MART objects incomplete or user-owned.
Every SQL in it verified live against a real installation.
Also fixes a latent reinstall loop: Studio's own install is CLEAN (no
example), which probes as 3 - but the previously-ready fast path accepted
only 0, so each ensure would have reconciled (reinstalled) forever. 0 and
3 are both READY there now.
* fix(marketplace): tab-switch loader shows the brand mark, not loading text
The Kits -> Catalog transition overlay used the draw-from-zero BrandLoader
(3.4s loop, stroke complete only at ~1.1s) with a Loading label at 40px:
in a short pending window the mark was near-invisible, so the user saw
plain loading TEXT.
BrandLoader gains an "instant" variant - the filled Exasol X from the
FIRST frame, kept alive by the pulsing glow, no label needed. The
marketplace nav overlay uses it at 56px; boot screens and long waits keep
the slow draw loop, which is right for them.
* fix(assistant): drop the redundant Save button from the SQL-grants shield
Closing the shield popover already commits staged toggles (dismiss IS the
save - a change is never silently lost), so the Save button in the sticky
header was a second path to the same action. Removed; a quiet "applies on
close" hint appears in its place while there are staged changes.
* fix(assistant): hide the engine synthetic tool echo from user messages
When the engine tools read a file, opencode appends SYNTHETIC parts to
the USER message ("Called the Read tool with the following input: {...}"
followed by the entire file body) - model-only context. The panel
rendered those parts as if the user typed them, and editing the message
seeded the input box with all of it.
Synthetic parts always TRAIL the typed text, so stripMachineContext now
also cuts everything from the first tool-echo marker on - which covers
the bubble, Copy, and the Markdown export in one place (they all strip
through it). The edit composer additionally rewrites its runtime-seeded
text on mount via the message-scoped composer client, so editing shows
only what the user actually wrote. Plain text that merely mentions a
tool is untouched (tested).
* fix(assistant): the panel agent can draft semantic models and resume its plan
The live transcript showed the panel agent proposing a TPCH semantic-model
plan, then giving up ("requires specific admin script syntax") and, when
the user said to finish the todo list, not even seeing its own open plan.
- New current_plan gateway tool: "continue / do it / finish the todo list"
now resolves to the OPEN plan and its per-step status (the route existed;
no tool exposed it).
- The drafting recipe is spelled out where the agent looks (semantic_models
tool description + scenario-router), including the one thing that makes
guessing unnecessary: SEMANTIC_CATALOG.ADMIN_SCRIPT_PARAMETERS lists
every admin script signature and call template - a plain SELECT away.
- semantic_admin (loop tool) points at the same signatures view.
Proven end to end on the live app sidecar: the TPCH draft model was built
through five approved plans (CREATE_MODEL, 7 entities with grains, 6
relationships with unique keys and key mappings, semantic object, 4
dimensions, facts + total_revenue metric via APPLY_SEMANTIC_DEFINITION,
VALIDATE_MODEL) - zero validation issues, TPCH no longer uncovered, DRAFT
status preserved (publishing stays a user decision).
* fix(editor): EXECUTE SCRIPT results reach the grid instead of "0 rows affected"
Running EXECUTE SCRIPT ... RETURNS TABLE in the SQL editor showed
"0 rows affected": is_result_set_statement only whitelisted the
SELECT family, so scripts went down the rowcount path and their result
table was silently discarded - the same bug class fixed in the DAG
executor earlier. EXECUTE now routes through the fetch path, which is
safe for BOTH script shapes (the driver streams a plain script rowcount
result as zero rows - verified in sqlx-exasol QueryResultStream).
Editor-run scripts also trigger the background semantic revalidation now
(is_semantic_impacting_script - Lua pquery can run DDL, SEMANTIC_ADMIN
scripts change the catalog itself), matching the agent-side rule, with
the sync own VALIDATE/REFRESH calls exempt so it can never self-trigger.
* fix(editor): complete + comment-proof routing of output-producing statements
Follow-up to the EXECUTE SCRIPT fix: the user asked which OTHER statement
shapes lose their output. Two were still misrouted to the rowcount path:
parenthesized queries ("(SELECT 1) UNION ...", no leading keyword) and
any query behind a same-line block comment ("/* hint */ SELECT ...",
which the crude line-based comment scan could not see past).
A full fetch_many unification (ask the database instead of classifying)
was BUILT AND REVERTED on Codex review: sqlx-exasol implements the only
both-kinds API via ExecuteBatch, whose own semicolon splitter would shred
CREATE SCRIPT bodies, and multi-result streams would misreport counts.
With single-statement routing required, a keyword head is the correct
mechanism - and the set is CLOSED under Exasol grammar (documented on the
classifier): SELECT / WITH / VALUES / parenthesized query / DESCRIBE /
DESC / EXPLAIN / EXECUTE SCRIPT.
- strip_leading_comments: iterative -- and /* */ stripping shared by the
router and the semantic classifiers (replaces the line-based scan)
- "(" prefix routes to the result-set path
- is_semantic_impacting_script: comment-proof, and the sync self-call
exemption now matches only DIRECT call targets - a wrapper script that
merely mentions VALIDATE_MODEL in an argument still triggers a pass
* docs(graph): refresh graphify for the P3-P5 + semantic-layer era
* feat(agent): close the last two P4 eval deferrals (import→query, clarify)
The spec listed both as deferred; both now exist as real golden cases.
- clarify expectation kind: the question names an undefined business term
("high-value orders"), so the only correct move is to ask for the
definition. Scoring needs a request for the MISSING thing plus a request
form - a "?" is not what makes it a request, and an offer made after
answering ("...would you like me to export them?") is not one either -
and an invented definition ("assuming high value means over 500") vetoes
the pass outright.
- attachments + approveWrites on a case: the runner hands suite files to
the turn, and a load→query case opts out of the blanket permission denial
the refusal case depends on. parseSuite REFUSES approveWrites together
with refusal/clarify: those assert nothing was written, but import_csv
writes without emitting SQL, so the combination is unsound as well as
contradictory.
- import-then-query asserts the query half too (run_sql + the table name),
not just the import, and creates its own schema so --case works on a
fresh database.
- Attachment fixtures are validated as fixtures: plain filenames only (the
name reaches the model-facing note and the document store), images must
be data: URLs, 256 KB cap.
Codex review found 3 real bugs (weak clarify detector, the unsound
approveWrites+refusal combination, order-coupled setup) and 2 minors
(attachment hardening, the unasserted query half) - all fixed here, each
with its example string as a regression test. 333 agent-core tests green.
Also refreshes the .ua file inventory (a full graph rebuild is still
pending - it needs a dedicated run).
* fix(drivers,runtime): stop drivers running as other drivers; unblock Personal 2.3
Investigation of the driver matrix and Exasol Personal 2.3-rc found two live
defects, both fixed here. The full plan is docs/platform-drivers-spec.md.
Drivers — a selected driver could silently run as a different one.
driver_runtime() declares node/go/r/dotnet runtimes that no bridge
implements, and the Python bridge's else-branch runs pyexasol: picking
"Exasol TS driver" opened a pyexasol connection and said nothing. Same class
as the "0 rows affected" script bug — a wrong answer, not a missing feature.
execute_via_driver now refuses an unimplemented driver by name, and
driver_status derives supported/hint from that SAME authority so the picker
can never offer what execution refuses. That also catches two the UI
previously claimed outright: sqlalchemy (ran plain pyexasol) and exarrow-rs
(ran the in-process sqlx driver).
Runtime — 2.3 would have broken local setup entirely. 2.3 makes local host
preparation (where it installs/prepares Podman) FAIL when it cannot prompt,
and Studio always drives the launcher non-interactively, so install/start
must pass --auto-approve. That flag does not exist before 2.3, where an
unknown flag is itself an error, so the launcher is asked what it accepts
via the existing `install --help` probe rather than assuming a version. The
decision is split from the subprocess and unit-tested.
121 Rust tests green.
* feat(drivers): TS driver runs natively; SQLAlchemy runs as SQLAlchemy
Two of the listed drivers now really execute, neither asking the user to
install anything.
TS driver — natively, on what the app already ships. Studio bundles a Node
runtime AND @exasol/exasol-driver-ts, so the new driver-bridge.cjs needs no
new runtime and no sidecar coupling: it speaks the same stdin/stdout JSON
contract as the Python bridge, and uses the driver's RAW mode so the
protocol itself reports resultSet vs rowCount — no guessing from statement
text, the same lesson as the editor routing fix.
SQLAlchemy — through its own dialect (exa+websocket) rather than the old
fallback to plain pyexasol, which was a different driver wearing its name.
exec_driver_sql keeps ':' in SQL from being read as a bind parameter, and
AUTOCOMMIT keeps DDL/DML from rolling back when the connection closes.
Live testing against a real database caught two defects that unit tests
could not have:
- the driver rejects with PLAIN OBJECTS, not Errors, so every failure
rendered as "[object Object]" and lost the database's message
- the raw envelope's status/exception were ignored, so a SELECT on a
missing table reported SUCCESS with 0 rows
Both fixed and covered by tests; mapRawResult is now a pure function so
every branch (error status, bare exception, missing results, resultSet,
rowCount, truncation) is unit-tested. Verified live: typed result sets with
NULL preserved, INSERT reporting 3 affected rows, maxRows truncation
flagged, and a failing statement returning "42000: object ... not found".
339 agent-core tests green. NOTE: cargo test could not run — the local
toolchain is blocked on an unaccepted Xcode license (sudo xcodebuild
-license accept); the Rust side passes cargo check and its logic is
unchanged apart from routing.
* Run exarrow, Go and R as real drivers; refuse ADO.NET for the real reason
Three of the drivers Studio listed could not actually run, and one was
listed as if it were only a matter of time.
exarrow-rs now runs IN-PROCESS. It is a Rust crate compiled into Studio,
so picking it installs nothing, and it returns Apache Arrow batches -
which is the point of choosing it: you see what an ADBC/pandas/Polars
client gets back. exarrow_exec.rs keeps the Arrow -> grid conversion
pure and tested: ints stay numbers, scaled DECIMALs stay exact text
(through f64 they would round), dates and timestamps render exactly like
the native driver, NULL is decided from the validity bitmap first, and
`truncated` is set only when a row was really dropped. Two live tests
prove it against a real database, including that a failing statement is
an ERROR and not a silent zero-row success.
Linking exarrow brings a second rustls crypto provider into the binary,
and with two compiled in rustls' automatic process-level lookup panics
instead of choosing. Studio installs ring as the default at startup -
ring, deliberately: sqlx names it explicitly and reqwest consults the
process default before falling back to it, so every existing TLS path
is untouched and only exarrow's own connections are newly resolved.
Go runs natively too: a prebuilt exasol-bridge-go binary, built by
scripts/build-driver-bridges.sh and shipped as a bundle resource (the
bridges directory is committed so the build cannot fail on a missing
resource; release CI builds the real binary per target). Live-proven -
typed result sets, exact decimals, real NULLs, real database errors, and
the batch stops at the first failure.
R is detected, not bundled: it is a large runtime that hard-codes its
own install paths, and the official `exasol` package compiles against
the Exasol ODBC driver. Studio ships the bridge script, finds Rscript,
installs the package into its OWN R library, and refuses clearly when R
is absent.
ADO.NET stays refused, and now says why: Exasol publishes it only as a
Windows MSI - the downloads index lists Windows alone and there is no
NuGet package - so it can never run here. A refusal that names the real
reason beats one that implies a backlog item.
Bridges no longer guess which statements return rows: Studio computes it
once with the same classifier the native path uses and sends it, so no
statement is ever run twice to find out. The Python bridge moved next to
the Go and R ones as real, readable Python, which also brings
driver_exec.rs back under 1,000 lines.
* Record the driver outcomes in the platform spec
Workstream A is done, so the spec should say what is true now instead of
what was true when it was written: which drivers execute through their
own runtime, which is detected rather than bundled and why, and which is
closed as impossible off Windows. The A4-A6 decision the spec was
holding open is settled and written down with its reasoning; the only
question left there is whether to build a Windows-only ADO.NET bridge,
which needs a Windows machine with the provider installed.
A9 is marked partial on purpose: exarrow's live checks are automated and
opt-in, TS and Go were proven by hand. Claiming a tier that does not
exist yet would be the same kind of lie the gate work removed.
* Send every R statement through dbSendQuery
r-exasol exports dbSendQuery and dbGetRowsAffected but not dbExecute or
dbSendStatement, so DBI's dbExecute would dispatch to a method that does
not exist: every INSERT, UPDATE and DDL through the R driver would have
failed. One path now serves both kinds — fetch rows, or read the
affected count off the same result.
The result handle is also released per statement. The `on.exit` it used
before sits in a loop at script top level, so it registers on the global
frame and nothing is freed until the script ends.
* Finish the R driver and prove every bridge against a real database
The R driver ran for the first time against a live database, and three
things only that could expose turned up.
The `exasol` package prints progress to STDOUT — "EXASOL driver loaded",
"Using temporary schema: TEMP_…_CREATED_BY_R". Studio parses stdout as
one JSON document, so a single s…
Exasol Studio v2026.8.1
Exasol Studio 2026.8.1
Exasol Studio v2026.8.0
Exasol Studio 2026.8.0
Exasol Studio v2026.7.1
Release 2026.7.1 (#133)
Exasol Studio v2026.7.0
Release 2026.7.0 (#130)
Exasol Studio v2026.6.1
Release 2026.6.1 (#89) Since 2026.6.0: web marketplace real installs, engine-hosted MCP gateway + AI clients in the browser, AI Personalization settings tab, universal search (⌘K) with fuzzy relevance, notebook cell pinning with apply-and-run, export multi-select, in-app Docs tab with theme sync, quiet compaction, friendly provider errors, instant connect/disconnect, zero native dropdowns. Engine baseline v2026.1.83.
Exasol Studio v2026.6.0
Release 2026.6.0 (#72) * Release 2026.6.0 The notebook artifact hub release: visual chart picker, per-cell database choice, System dashboards in the notebook, exa prompt seam, Dashboards surface retired, exa-engine submodule. Spec archived. * In-app Docs tab, guide deep links, session-delete sync - New Docs tab renders the served documentation INSIDE the app (iframe of the engine's /docs/studio; web build uses same-origin). Header has "Open in browser" and an explicit "exa docs ↗" that always opens the engine notebook externally. Title-bar Docs button targets the tab. - Welcome "Guides" entries deep-link to their exact docs pages (getting-started, connections, workbench/files, workbench/sql-editor, connections/drivers) in the Docs tab instead of one generic surface. - Deleting a chat now syncs everywhere: the panel dispatches exa:session-deleted, the open thread starts fresh when it was showing the deleted session, and the persisted active session is validated against the engine's list on every refresh (a chat deleted elsewhere or a stale localStorage entry can't reopen).
Exasol Studio v2026.5.0
Tauri 2 + React 19 desktop and web Exasol IDE with the exa AI engine: SQL workbench, schema browser, dashboards, marketplace, assistant with personas and structured questions, connection management, and docs. History starts fresh at this release.
json-tables (prebuilt by CI)
Prebuilt JSON Tables ingest engine (all distributions) + Python wheel,
built once from exasol-labs/exasol-json-tables so users need no Rust/cargo.
version=c123439080e8