latticesql 4.3.7 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -74,6 +74,8 @@ provenance** + a **trust/verification** workflow), reliability (**`withRetry`**
74
74
  seamless **keyless cloud file-byte access** (an in-database SigV4 presigner). All
75
75
  opt-in per table/call; absent the opt-in, behavior is byte-identical to 4.0.
76
76
 
77
+ **New in 5.0 (major release).** 5.0 reframes the GUI around an **Analytics view** — ask a question about your data and Lattice answers, building a **dashboard** when a picture answers best (the docked assistant is **"Ask Lattice"**). It adds **connected external data**: link a Postgres/other database or an **MCP server** and its tables mirror in as **typed, read-only** tables grouped under the source's name; **import intelligence** (`import_spreadsheet`, faithful every-row import, multi-table Excel sheets); **computed tables** (live read-only projections); and a rebuilt **chat** that acknowledges instantly and streams the answer in the background (reload never cancels an in-flight turn). **Breaking / consumer-facing changes:** the bespoke **Jira/Trello connector exports are removed** (connectors are now MCP-based); the per-user **Anthropic API-key** assistant path is retired in favor of built-in **OAuth** plus a generic **"Other AI Endpoint"** provider (base URL + key + model); and the inference-aggressiveness slider is gone (a fixed high default). Existing 3.0+/4.x configs and data silently upgrade on GUI open. Under the hood, four substrate pieces also land: (1) A **native vector search substrate**: the pgvector / sqlite-vec index now **stays in sync with writes** (inserts/updates/deletes mirror incrementally; a freshness guard falls back to an exact scan rather than ever serving a stale index), **semantic + hybrid search work for scoped cloud members** confined to the rows they may see (via a `SECURITY DEFINER` path keyed on the member's own role — an exact scan with no over-fetch inference channel), and the index is **tunable + observable** (`embeddings.index = { m, efConstruction }`, query-time `efSearch`, an internal index registry, and `lattice reindex` / `lattice index status` / `lattice doctor --fix`), and can be stored at **16-bit half precision** (`embeddings.index.quantization = 'halfvec'`, pgvector ≥ 0.7) to roughly halve its memory while the exact-scan fallback stays full precision. All opt-in; omit the knobs and the build/query is byte-identical to before. (2) A **live force-directed brain graph** across all three GUI graph surfaces (schema, per-object, and folder) — a dependency-free physics engine + live SVG renderer with drag-to-pin, zoom, neighbor highlight, and fly-in growth. (3) **Auto-update is now visible on every surface** (npm, desktop, and dev builds — previously only the npm-supervised CLI surfaced the pill), a long-open desktop window notices new releases on its own, and a `--no-auto-update` / `LATTICE_NO_AUTO_UPDATE=1` switch (default on) pins the version for testing, air-gapped, and reproducible-demo runs. (4) **Data provenance + lineage.** Every object's page now traces where its data came from across three tiers — **raw** (files / connectors), **computed** (imports / artifacts), and **observation** (AI / learning-loop edits) — via `GET /api/provenance`, shown as a force-directed graph or a grouped source table, with a per-row provenance panel; an additive internal lineage substrate (`__lattice_lineage` + an audit `source` column) records source→object edges. Relatedly, the brain graph now shows **object↔object relationships only** (`files` is a source, not a node) and the sidebar groups are collapsible. See **[docs/retrieval.md](docs/retrieval.md)** and **[docs/desktop.md](docs/desktop.md)**.
78
+
77
79
  **New in 4.0 (major release — mostly drop-in):** a major version that decomposes the three largest internal modules and hardens the cloud path for many simultaneous users, while keeping the `Lattice` / GUI surface stable. **Existing 3.0+ configs and databases are migrated forward SILENTLY on open** — you usually need to do nothing. The breaking changes are auto-handled on open (or clearly documented): the per-field `ref:` shorthand is still parsed (and the GUI rewrites it to the explicit `relations:` block on disk); a legacy empty-string `deleted_at` is normalized to `NULL`; a legacy `files.path`-only row is backfilled into the reference model; the render manifest is v2-only and self-upgrades on first render. The one consumer-code change: the exported `MEMBER_GROUP` constant is replaced by **`memberGroupFor(db)`** (the member group role is now **per-cloud** — derived from the database/schema — so unrelated clouds on one Postgres cluster no longer share a group). Opening a cloud workspace is now **much faster** (one batched schema introspection instead of per-table round-trips; the owner-side RLS/grant convergence runs in the background since the owner is BYPASSRLS). See **[docs/MIGRATING-4.0.md](docs/MIGRATING-4.0.md)** for the (mostly no-op) migration and the manual steps for library/non-GUI consumers.
78
80
 
79
81
  **New in 3.4:** the browser GUI now **updates itself** — launched from an npm install it silently installs the latest published version and keeps checking in the background, relaunching on the same port while the open tab auto-reloads onto the new build (a git checkout / `npx` copy is left untouched); **file loopback** — editing a rendered `.md` context file on disk flows back into the database through the normal write path (changelog/versioned/undoable, live in the GUI), with a public [`reverseSyncFromFiles()`](docs/api-reference.md) for embedders; on a cloud, a member's **rendered context is now scoped to their own visibility** (rendered through their RLS connection + masking view, so their assistant only ever reads rows they may see); the assistant gains a **`get_row_context`** tool (reads a record's pre-joined rendered context in one call) and **`add_column`** (add a field to an existing table on request); and the cloud gets resilience + search fixes — the open-time converge is **per-table fault-isolated** (one un-manageable table no longer breaks the whole workspace), `migrate-to-cloud` now builds the full-text index (with a public [`rebuildFtsIndexes()`](docs/api-reference.md)), and a plaintext `postgres://` URL in a config is healed into an encrypted credential reference on open. New `GET /api/version`, `GET /api/update/status`, and `POST /api/workspaces/reload` endpoints. See [docs/workspaces.md](docs/workspaces.md), [docs/cloud.md](docs/cloud.md), and [docs/assistant.md](docs/assistant.md).
@@ -122,7 +124,7 @@ opt-in per table/call; absent the opt-in, behavior is byte-identical to 4.0.
122
124
  - [Pluggable backends (v1.6+)](#pluggable-backends-v16)
123
125
  - [Architecture](#architecture)
124
126
  - [Examples](#examples)
125
- - [Connectors (v4.3+)](#connectors-v43)
127
+ - [MCP Connectors](#mcp-connectors)
126
128
  - [Staying up to date](#staying-up-to-date)
127
129
  - [Auto-update](#auto-update-v11)
128
130
  - [Telemetry](#telemetry)
@@ -1090,6 +1092,25 @@ await db.delete('tasks', 'task-001');
1090
1092
  await db.delete('event_seats', { event_id: 'e-1', seat_no: 3 });
1091
1093
  ```
1092
1094
 
1095
+ #### `transaction()` (v5.0+)
1096
+
1097
+ ```typescript
1098
+ await db.transaction<T>(fn: () => Promise<T>): Promise<T>
1099
+ ```
1100
+
1101
+ Run `fn` inside a single database transaction. Every write `fn` performs through this Lattice (`insert` / `update` / `delete` and their audit + changelog writes) commits together, or rolls back together if `fn` throws. Reads inside `fn` see its own uncommitted writes (read-your-writes). The transaction is scoped to the async context of `fn`, so two concurrent callers never accidentally share one; a nested `transaction()` reuses the outer transaction. When the adapter cannot open a transaction, `fn` runs without one.
1102
+
1103
+ ```typescript
1104
+ // Move every row from one table to another as one atomic unit — either all
1105
+ // rows move, or (on any error) nothing does.
1106
+ await db.transaction(async () => {
1107
+ for (const row of rows) {
1108
+ await db.insert('archive', row);
1109
+ await db.delete('inbox', row.id);
1110
+ }
1111
+ });
1112
+ ```
1113
+
1093
1114
  #### `get()`
1094
1115
 
1095
1116
  ```typescript
@@ -2166,11 +2187,14 @@ npx lattice gui
2166
2187
  npx lattice gui --config ./lattice.config.yml --output ./context --port 4317
2167
2188
  ```
2168
2189
 
2169
- ### File-system workspace (v2.0+)
2190
+ ### GUI layout (Analytics view + workspace)
2170
2191
 
2171
- By default the GUI is a **file-system-style workspace**. The home dashboard shows
2172
- one card per object; clicking in opens that object's rows as a grid of **folder
2173
- tiles** rather than a spreadsheet. Click a tile to open an **item view** that
2192
+ **In 5.0 the GUI opens on the Analytics view** a Dashboards sidebar, a tabbed
2193
+ dashboard canvas, and the persistent **Ask Lattice** dock. Ask a question and Lattice
2194
+ answers there, building a dashboard when a picture answers best; the wrench toggles the
2195
+ **Configure** drawer (Data Model / Inputs / Workspace). The data-browsing surface below
2196
+ is unchanged and reachable from the sidebar: each object's rows open as a grid of
2197
+ **folder tiles** rather than a spreadsheet. Click a tile to open an **item view** that
2174
2198
  renders the row as a document built from its columns — long-form fields render as
2175
2199
  formatted markdown — alongside that row's relationships as **sub-folders** you can
2176
2200
  keep opening (e.g. _Authors → a person → Books → a book → Reviews_). A breadcrumb
@@ -2211,12 +2235,17 @@ across 9 tables") instead of a wall of near-identical rows. The feed is scoped t
2211
2235
  the open conversation: the assistant's data changes are saved with each turn and
2212
2236
  replayed as those cards when you reopen the chat. When the assistant references a record, it emits an inline object-link pill — a clickable chip that opens that row in the mode-aware navigator.
2213
2237
 
2214
- Add a Claude API token in **User Settings → Assistant** (or set
2215
- `ANTHROPIC_API_KEY`) to enable the **AI assistant**: ask questions about your
2216
- data or instruct edits in natural language. The assistant calls the same
2217
- operations the UI does, so its changes are audited, shown in the feed, and
2218
- undoable. A Claude subscription can be connected instead via OAuth when the
2219
- `ANTHROPIC_OAUTH_*` environment variables are configured.
2238
+ Connect the **AI assistant** in **User Settings → Assistant** to ask questions
2239
+ about your data or instruct edits in natural language. The assistant calls the
2240
+ same operations the UI does, so its changes are audited, shown in the feed, and
2241
+ undoable. The built-in path is **Connect with Claude** (a subscription OAuth flow
2242
+ no env setup). Or point it at **any endpoint** via **Other AI Endpoint** — an
2243
+ OpenAI-compatible `chat/completions` server (OpenAI, Azure, OpenRouter, a local
2244
+ vLLM / Ollama / LM Studio server, or your own gateway) OR a Claude-API endpoint
2245
+ (the Anthropic wire is auto-selected for an Anthropic host) — the first-run screen
2246
+ takes a base URL, API key, and model, and every assistant feature then runs on it.
2247
+ (A managed deployment supplies the operator's model key via `ANTHROPIC_API_KEY`
2248
+ env; the per-user "paste an Anthropic key" path was retired in 5.0.)
2220
2249
 
2221
2250
  Optional extras, each enabled by its own key/binary:
2222
2251
 
@@ -2389,10 +2418,14 @@ a **Context Constructor** that turns dropped files and pasted text into linked
2389
2418
  Lattice objects. It is **GUI-only and inert until you configure a credential** —
2390
2419
  the library API is unchanged and fully backwards-compatible.
2391
2420
 
2392
- - **Connect Claude.** Paste an Anthropic API key in **Settings User → Assistant**
2393
- (or set `ANTHROPIC_API_KEY`); it's stored encrypted in the native `secrets`
2394
- entity. A subscription **Connect** link (PKCE) appears when the `ANTHROPIC_OAUTH_*`
2395
- values are set (see [`.env.example`](.env.example)).
2421
+ - **Connect the assistant.** The built-in path is **Connect with Claude** a
2422
+ subscription OAuth flow (PKCE) in **Settings User Assistant**, no env setup
2423
+ required. To bring your own model instead, **Other AI Endpoint** takes a base URL +
2424
+ API key + model (an OpenAI-compatible endpoint, or a Claude-API endpoint — the
2425
+ Anthropic wire is auto-selected for an Anthropic host); the credential is stored
2426
+ encrypted in the native `secrets` entity. (The former "paste an Anthropic API key"
2427
+ path was retired in 5.0 — a managed deployment supplies the operator key via
2428
+ `ANTHROPIC_API_KEY` env instead.)
2396
2429
  - **Drop files / paste text / images / URLs.** Sources become native `files` rows
2397
2430
  (referenced, not copied) and are extracted — documents (PDF / Office /
2398
2431
  OpenDocument / EPUB / RTF) parsed **natively in-process**, **images via Claude
@@ -2411,8 +2444,9 @@ the library API is unchanged and fully backwards-compatible.
2411
2444
  the optional `playwright` dependency is installed (graceful static fallback
2412
2445
  otherwise). Tunable via the `LATTICE_URL_*` env vars — see
2413
2446
  [`.env.example`](.env.example).
2414
- - **Inference Aggressiveness** slider tunes how much the assistant extrapolates
2415
- (temperature + link liberality + auto-junction/auto-create gating).
2447
+ - **Inference** the assistant extrapolates (link liberality + auto-junction/auto-create
2448
+ gating) at a fixed high default. (The earlier user-facing "Inference Aggressiveness"
2449
+ slider was removed in 5.0.)
2416
2450
  - **Context-aware.** The chat knows the record you're viewing, so "delete this
2417
2451
  file" / "summarize this" resolve to it; and it can answer questions about Lattice
2418
2452
  itself (e.g. "what is private mode?") via a `lattice_help` tool that searches
@@ -2805,6 +2839,8 @@ if (result.restartRequired) {
2805
2839
 
2806
2840
  `autoUpdate()` is safe to call on every startup — it skips if already on the latest version. Pass `{ quiet: true }` to suppress console output.
2807
2841
 
2842
+ The **GUI and desktop app** auto-update on their own: `lattice gui` (when installed via npm) installs newer versions and relaunches in the background, and the desktop app applies updates on relaunch. Both show an "Update available" link next to the version chip when a newer release is published. To pin to the current version, pass `lattice gui --no-auto-update` or set `LATTICE_NO_AUTO_UPDATE=1` (the latter also covers the desktop app, which has no CLI flags).
2843
+
2808
2844
  **`AutoUpdateResult`**
2809
2845
 
2810
2846
  ```typescript
@@ -2858,76 +2894,62 @@ See Scarf's own [privacy documentation](https://docs.scarf.sh) for the upstream
2858
2894
 
2859
2895
  ---
2860
2896
 
2861
- ## Connectors (v4.3+)
2897
+ ## MCP Connectors
2862
2898
 
2863
2899
  Connectors sync data from external systems into Lattice as **connected data
2864
2900
  types** — tables whose rows are ingested from a source rather than authored
2865
- locally. A connector talks to one external product directly using your own
2866
- credentials. The built-in connector is **Jira**, which talks to Jira Cloud's
2867
- REST + Agile APIs via [`jira.js`](https://github.com/MrRefactoring/jira.js) — no
2868
- broker service, no extra API key.
2869
-
2870
- `jira.js` is an **optional dependency** install it only to use the connector:
2871
-
2872
- ```bash
2873
- npm install jira.js
2874
- ```
2875
-
2876
- Connect, sync, and disconnect programmatically:
2901
+ locally. Every connector is a standard **MCP connection**: Lattice runs as a
2902
+ local [Model Context Protocol](https://modelcontextprotocol.io) client and
2903
+ talks to the server directly from your machine — no broker, no provider-specific
2904
+ code. A provider is just another MCP server URL.
2905
+
2906
+ In the **GUI**, open **Configure MCP Connectors**: paste a server URL, sign in
2907
+ with the provider's own OAuth in your browser (client identity via a client-ID
2908
+ metadata document, dynamic registration, or a pre-registered client id — the
2909
+ form asks only when the server requires one). Each server is **introspected at
2910
+ connect and modeled into typed, read-only tables — one per record kind** (scalar
2911
+ fields become real columns, nested data spills to a `data` JSON column),
2912
+ namespaced per connection and grouped under a **per-server header named for the
2913
+ server's brand** (e.g. an MCP server at `mcp.acme.com` → an **ACME** group with its
2914
+ own tables). A server that exposes nothing modelable falls back to a single flat
2915
+ `mcp_items` table. Connect any number of servers side by side; each row shows
2916
+ status, last sync, and Refresh / Disconnect / Reconnect. Tokens are stored
2917
+ encrypted on your machine and synced data stays local.
2918
+
2919
+ Programmatically, the same engine is exposed as a library surface:
2877
2920
 
2878
2921
  ```typescript
2879
2922
  import {
2880
- JiraConnector,
2923
+ genericConnector,
2881
2924
  createConnector,
2882
2925
  syncConnector,
2883
2926
  syncIfStale,
2884
2927
  disconnectConnector,
2885
2928
  } from 'latticesql';
2886
2929
 
2887
- const connector = new JiraConnector();
2888
-
2889
- // 1. Validate + store the member's Atlassian credentials (site + email + API token):
2890
- const { connectionId, displayName } = await connector.connect({
2891
- site: 'https://your-domain.atlassian.net',
2892
- email: 'you@example.com',
2893
- apiToken: process.env.JIRA_API_TOKEN!,
2894
- });
2895
-
2896
- // 2. Register + sync (defines the six jira_* connected tables and ingests):
2930
+ const connector = genericConnector();
2897
2931
  const connectorId = await createConnector(db, {
2898
- connector: 'jira',
2899
- toolkit: 'jira',
2900
- displayName: displayName ?? 'jira',
2901
- connectionRef: connectionId,
2932
+ connector: 'mcp',
2933
+ toolkit: 'mcp',
2934
+ displayName: 'My MCP server',
2935
+ connectionRef: connectionId, // from the OAuth/stdio connect flow
2902
2936
  connectedBy: 'user-123',
2903
2937
  });
2904
- await syncConnector(db, connector, connectorId);
2905
-
2906
- // 3. Keep fresh no scheduler: re-sync on load if older than an hour.
2907
- await syncIfStale(db, connector, connectorId);
2908
-
2909
- // 4. Disconnect — soft-deletes the ingested rows + drops the stored credentials.
2910
- await disconnectConnector(db, connector, connectorId);
2938
+ await syncConnector(db, connector, connectorId); // ingest (idempotent upserts)
2939
+ await syncIfStale(db, connector, connectorId); // re-sync on load if > 1h old
2940
+ await disconnectConnector(db, connector, connectorId); // soft teardown
2911
2941
  ```
2912
2942
 
2913
- Connected tables (`jira_projects`, `jira_issues`, `jira_comments`, `jira_users`,
2914
- `jira_boards`, `jira_sprints`) are full Lattice tables: queryable, full-text
2915
- searchable, rendered to context, and linked on the graph (issue → project,
2916
- comment → issue, …). Each row carries immutable lineage (`_source_connector_id`,
2917
- `_source_model`); a re-sync upserts on the natural key and soft-deletes rows that
2918
- vanished from the source.
2919
-
2920
- On a cloud workspace, the owner calls `enableConnectorRls(db, connector, 'jira')`
2921
- to scope connected rows per member (private by default, or shared per type).
2943
+ Connected rows are full Lattice rows: queryable, full-text searchable, rendered
2944
+ to context, per-member `private` by default on a cloud
2945
+ (`enableConnectorRls(db, connector, 'mcp')`), and stamped with immutable lineage
2946
+ (`_source_connector_id`, `_source_model`). A re-sync upserts on the natural key
2947
+ and soft-deletes rows that vanished from the source.
2922
2948
 
2923
- In the **GUI**, all of this is point-and-click: open **Settings → Connectors**,
2924
- enter your Jira site URL + email + API token, and connect / refresh / disconnect.
2925
- Connected data types show a "Connected" badge in the Objects list.
2926
-
2927
- See [docs/connectors.md](docs/connectors.md) for the full guide and the connector
2928
- SPI (to add a new connector).
2929
-
2930
- ---
2949
+ See [docs/connectors.md](docs/connectors.md) for the full guide the OAuth
2950
+ client-identity mechanisms, the privacy model, the per-server typed-table
2951
+ modeling (with the `mcp_items` fallback), and the connector SPI for embedding a
2952
+ specific provider.
2931
2953
 
2932
2954
  ## Contributing
2933
2955