latticesql 4.3.8 → 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/dist/index.d.ts CHANGED
@@ -54,10 +54,40 @@ interface RenderCursor {
54
54
  grants: string | null;
55
55
  owners: string | null;
56
56
  }
57
+ /** A rendered file Lattice wrote outside the entity-context trees (a phase-1
58
+ * table rollup), tracked so it has a lifecycle: when its path changes or its
59
+ * table disappears, the old path moves to `retiredFiles` and is pruned by the
60
+ * next reconciliation — hash-guarded so a user-edited file is never deleted. */
61
+ interface TableFileManifestInfo {
62
+ /** Path relative to the output dir (the compiled def.outputFile). */
63
+ path: string;
64
+ /** SHA-256 hex digest of the last content Lattice rendered to it. */
65
+ hash: string;
66
+ }
57
67
  interface LatticeManifest {
58
68
  version: 1 | 2;
59
69
  generated_at: string;
60
70
  entityContexts: Record<string, EntityContextManifestEntry>;
71
+ /**
72
+ * Phase-1 table rollup files, keyed by table (see {@link TableFileManifestInfo}).
73
+ * Optional — a v3 manifest has no rollup history, and reconciliation treats the
74
+ * absence as "prune nothing" (never as delete-everything).
75
+ */
76
+ tableFiles?: Record<string, TableFileManifestInfo>;
77
+ /**
78
+ * Phase-2 multi-output files, keyed by multi name: every path the multi
79
+ * produced on its last render, with content hashes. A key that disappears (a
80
+ * deleted row) retires its path into `retiredFiles` on the next render.
81
+ */
82
+ multiFiles?: Record<string, Record<string, string>>;
83
+ /**
84
+ * Rendered files whose path is no longer produced by the current schema (an
85
+ * outputFile change, a dropped table). Each entry persists here until the
86
+ * reconciliation pass actually deletes it (only when the on-disk content still
87
+ * hashes to Lattice's own last write) or the file disappears — so a crash
88
+ * between the manifest write and the prune can never orphan a file forever.
89
+ */
90
+ retiredFiles?: TableFileManifestInfo[];
61
91
  /**
62
92
  * Render-output format version the tree was produced with (see
63
93
  * {@link TEMPLATE_VERSION}). Optional: a manifest written before this field
@@ -836,6 +866,287 @@ declare class ConnectedSourceImmutableError extends Error {
836
866
  constructor(table: string, column: string);
837
867
  }
838
868
 
869
+ /**
870
+ * Scalar types recognised in `lattice.config.yml` field definitions.
871
+ *
872
+ * | YAML type | SQLite type | TypeScript type |
873
+ * | ----------- | ----------- | --------------- |
874
+ * | `uuid` | TEXT | string |
875
+ * | `text` | TEXT | string |
876
+ * | `integer` | INTEGER | number |
877
+ * | `int` | INTEGER | number |
878
+ * | `real` | REAL | number |
879
+ * | `float` | REAL | number |
880
+ * | `boolean` | INTEGER | boolean |
881
+ * | `bool` | INTEGER | boolean |
882
+ * | `datetime` | TEXT | string |
883
+ * | `date` | TEXT | string |
884
+ * | `blob` | BLOB | Buffer |
885
+ */
886
+ type LatticeFieldType = 'uuid' | 'text' | 'integer' | 'int' | 'real' | 'float' | 'boolean' | 'bool' | 'datetime' | 'date' | 'blob';
887
+ /**
888
+ * A single field (column) definition in a `lattice.config.yml` entity.
889
+ *
890
+ * @example
891
+ * ```yaml
892
+ * id: { type: uuid, primaryKey: true }
893
+ * title: { type: text, required: true }
894
+ * status: { type: text, default: open }
895
+ * assignee_id: { type: uuid }
896
+ * score: { type: integer, default: 0 }
897
+ * ```
898
+ */
899
+ interface LatticeFieldDef {
900
+ /** Column data type */
901
+ type: LatticeFieldType;
902
+ /** Mark this column as the table's primary key */
903
+ primaryKey?: boolean;
904
+ /** Column is NOT NULL */
905
+ required?: boolean;
906
+ /** SQL DEFAULT value */
907
+ default?: string | number | boolean;
908
+ /**
909
+ * Per-column audience (Stage-0 scaffolding for the per-viewer enrichment
910
+ * model). Names who may see this column's value in a cloud. Omitted ⇒
911
+ * `row-audience` — the value is visible to exactly whoever can see the row,
912
+ * which is today's behavior, so leaving it unset changes nothing. Later
913
+ * stages parse a richer grammar (e.g. `subject+role:hr`) and generate a
914
+ * cell-masking view from it; Stage-0 only records the metadata.
915
+ */
916
+ audience?: string;
917
+ /**
918
+ * DEPRECATED (3.x) per-field foreign-key shorthand: `ref: <targetTable>` declared
919
+ * a `belongsTo` whose relation name is the field name with a trailing `_id`
920
+ * stripped. Superseded by the explicit entity-level `relations:` block. 4.0 still
921
+ * PARSES it (converted to a `belongsTo` in-memory) so existing 3.0+ configs keep
922
+ * working, and the GUI silently rewrites it to `relations:` on open so configs
923
+ * migrate forward. A future major may drop this once configs have upgraded.
924
+ */
925
+ ref?: string;
926
+ /**
927
+ * Marks this field as a COMPUTED column: a real, materialized column on the base
928
+ * table whose value is derived (not hand-entered) using one of the computed field
929
+ * kinds (alias / calc / ai_classify / ai_transform / aggregate — the same vocabulary
930
+ * the retired computed-TABLE views used). Because it is a real column it is
931
+ * queryable/filterable/sortable and appears in `SELECT *`. A field is computed iff it
932
+ * carries `computed:`; deterministic kinds recompute synchronously on write, AI kinds
933
+ * fill asynchronously. See src/schema/computed-field.ts.
934
+ */
935
+ computed?: ComputedFieldDef;
936
+ }
937
+ /**
938
+ * Inline render spec inside YAML — a flat object alternative to `TemplateRenderSpec`.
939
+ *
940
+ * @example
941
+ * ```yaml
942
+ * render:
943
+ * template: default-list
944
+ * formatRow: "{{title}} — {{status}}"
945
+ * ```
946
+ */
947
+ interface LatticeEntityRenderSpec {
948
+ template: string;
949
+ formatRow?: string;
950
+ }
951
+ /**
952
+ * A single entity (table) definition in `lattice.config.yml`.
953
+ *
954
+ * @example
955
+ * ```yaml
956
+ * ticket:
957
+ * fields:
958
+ * id: { type: uuid, primaryKey: true }
959
+ * title: { type: text, required: true }
960
+ * assignee_id: { type: uuid }
961
+ * relations:
962
+ * assignee:
963
+ * type: belongsTo
964
+ * table: user
965
+ * foreignKey: assignee_id
966
+ * render: default-list
967
+ * outputFile: context/TICKETS.md
968
+ * ```
969
+ */
970
+ interface LatticeEntityDef {
971
+ /** Column definitions */
972
+ fields: Record<string, LatticeFieldDef>;
973
+ /**
974
+ * Explicit `belongsTo` relations for this entity, keyed by relation name.
975
+ *
976
+ * Each entry declares a foreign key on THIS entity pointing at another
977
+ * table: `{ type: belongsTo, table, foreignKey, references? }`. The
978
+ * `foreignKey` names a plain field on this entity; `references` is the
979
+ * column on the related table (defaults to its primary key). The relation
980
+ * name (the map key) is whatever you choose — it is not derived from the
981
+ * field name.
982
+ *
983
+ * @example
984
+ * ```yaml
985
+ * relations:
986
+ * assignee:
987
+ * type: belongsTo
988
+ * table: user
989
+ * foreignKey: assignee_id
990
+ * # references: id # optional; defaults to the target's primary key
991
+ * ```
992
+ */
993
+ relations?: Record<string, BelongsToRelation>;
994
+ /**
995
+ * How to render rows into context text.
996
+ * Accepts the same forms as `TableDefinition.render`:
997
+ * - A `BuiltinTemplateName` string (e.g. `default-list`)
998
+ * - A `{ template, formatRow }` object for hooks
999
+ */
1000
+ render?: string | LatticeEntityRenderSpec;
1001
+ /** Render output file path (relative to the config file directory) */
1002
+ outputFile: string;
1003
+ /**
1004
+ * Optional explicit primary key override.
1005
+ * If omitted, the field with `primaryKey: true` is used.
1006
+ * Accepts a single column name or an array for composite keys.
1007
+ */
1008
+ primaryKey?: string | string[];
1009
+ }
1010
+ /**
1011
+ * A single field of a computed table. Every field has a direct computation:
1012
+ *
1013
+ * - `alias` — project a base column (`'status'`) or a dotted belongsTo path
1014
+ * (`'assignee.team.name'`, resolved through declared relations).
1015
+ * - `calc` — a sandboxed calculation expression over base columns / belongsTo
1016
+ * paths (see `schema/calc-expr.ts` for the grammar). `type` is the display
1017
+ * type; defaults to `text`.
1018
+ * - `ai_classify` — a model-assigned label for the field's `input` value,
1019
+ * constrained to `labels`. Model outputs are materialized once into the
1020
+ * `__lattice_ai_map` bookkeeping table and LEFT JOINed by the view — the
1021
+ * model is never re-run at read time.
1022
+ * - `ai_transform` — a model-derived free-form value over `inputs` (order is
1023
+ * part of the cache identity). Materialized into `__lattice_ai_cell` per
1024
+ * row and LEFT JOINed; a changed source row makes the join miss, so the
1025
+ * field reads NULL until the next fill pass — never a stale value.
1026
+ * - `aggregate` — fold many junction rows into one scalar per base row via a
1027
+ * correlated subquery. `via` is `'<junctionTable>.<remoteRelationOrTable>'`
1028
+ * (e.g. `'ticket_tags.tag'`); `column` names the remote column to aggregate
1029
+ * (required for every `fn` except `count`).
1030
+ */
1031
+ type ComputedFieldDef = {
1032
+ kind: 'alias';
1033
+ source: string;
1034
+ } | {
1035
+ kind: 'calc';
1036
+ expr: string;
1037
+ type?: LatticeFieldType;
1038
+ } | {
1039
+ kind: 'ai_classify';
1040
+ input: string;
1041
+ prompt: string;
1042
+ labels: string[];
1043
+ model?: 'default' | 'cheapest';
1044
+ } | {
1045
+ kind: 'ai_transform';
1046
+ inputs: string[];
1047
+ prompt: string;
1048
+ model?: 'default' | 'cheapest';
1049
+ } | {
1050
+ kind: 'aggregate';
1051
+ via: string;
1052
+ fn: 'count' | 'sum' | 'avg' | 'min' | 'max' | 'concat';
1053
+ column?: string;
1054
+ };
1055
+ /**
1056
+ * A computed table: a live, read-only SQL VIEW defined over one base table
1057
+ * (a declared entity or another computed table). The view always projects the
1058
+ * base primary key as `id` first, then each field in declaration order.
1059
+ * Registered at init as a queryable, non-writable table.
1060
+ *
1061
+ * @example
1062
+ * ```yaml
1063
+ * computed:
1064
+ * ticket_summary:
1065
+ * base: ticket
1066
+ * fields:
1067
+ * title: { kind: alias, source: title }
1068
+ * team: { kind: alias, source: assignee.team.name }
1069
+ * is_urgent: { kind: calc, expr: "priority >= 3", type: boolean }
1070
+ * tag_count: { kind: aggregate, via: ticket_tags.tag, fn: count }
1071
+ * ```
1072
+ */
1073
+ interface ComputedTableDef {
1074
+ /** Base table: a declared entity or another computed table. */
1075
+ base: string;
1076
+ /** Optional human description (display metadata only). */
1077
+ description?: string;
1078
+ /** Field definitions, projected in declaration order. */
1079
+ fields: Record<string, ComputedFieldDef>;
1080
+ }
1081
+ /**
1082
+ * The top-level `lattice.config.yml` document.
1083
+ *
1084
+ * @example
1085
+ * ```yaml
1086
+ * db: ./data/app.db
1087
+ * entities:
1088
+ * ticket:
1089
+ * fields:
1090
+ * id: { type: uuid, primaryKey: true }
1091
+ * title: { type: text, required: true }
1092
+ * render: default-list
1093
+ * outputFile: context/TICKETS.md
1094
+ * ```
1095
+ */
1096
+ interface LatticeConfig {
1097
+ /** Path to the SQLite database file (relative to the config file) */
1098
+ db: string;
1099
+ /** Entity (table) definitions */
1100
+ entities: Record<string, LatticeEntityDef>;
1101
+ /** Entity context directory definitions */
1102
+ entityContexts?: Record<string, LatticeEntityContextDef>;
1103
+ /** Computed-table (read-only SQL projection) definitions */
1104
+ computed?: Record<string, ComputedTableDef>;
1105
+ }
1106
+ /**
1107
+ * Source spec in YAML config — either the shorthand string 'self' or an object.
1108
+ */
1109
+ type LatticeEntityContextSourceDef = 'self' | {
1110
+ type: 'hasMany';
1111
+ table: string;
1112
+ foreignKey: string;
1113
+ references?: string;
1114
+ } | {
1115
+ type: 'manyToMany';
1116
+ junctionTable: string;
1117
+ localKey: string;
1118
+ remoteKey: string;
1119
+ remoteTable: string;
1120
+ references?: string;
1121
+ } | {
1122
+ type: 'belongsTo';
1123
+ table: string;
1124
+ foreignKey: string;
1125
+ references?: string;
1126
+ };
1127
+ /** A single per-entity file spec in YAML config */
1128
+ interface LatticeEntityContextFileDef {
1129
+ source: LatticeEntityContextSourceDef;
1130
+ template: string;
1131
+ budget?: number;
1132
+ omitIfEmpty?: boolean;
1133
+ }
1134
+ /** Entity context definition in YAML config */
1135
+ interface LatticeEntityContextDef {
1136
+ slug: string;
1137
+ directoryRoot?: string;
1138
+ protectedFiles?: string[];
1139
+ index?: {
1140
+ outputFile: string;
1141
+ render: string;
1142
+ };
1143
+ files: Record<string, LatticeEntityContextFileDef>;
1144
+ combined?: {
1145
+ outputFile: string;
1146
+ exclude?: string[];
1147
+ };
1148
+ }
1149
+
839
1150
  /**
840
1151
  * Declarative computed columns + materialized rollups.
841
1152
  *
@@ -1484,6 +1795,15 @@ interface TableDefinition {
1484
1795
  * `refreshMaterializedRollups`. See {@link MaterializedRollupSpec}.
1485
1796
  */
1486
1797
  materializedRollups?: Record<string, MaterializedRollupSpec>;
1798
+ /**
1799
+ * Serializable computed COLUMNS (5.0+) — a real, materialized column derived by a
1800
+ * computed field kind (alias / calc / ai_classify / ai_transform / aggregate; the
1801
+ * same vocabulary the retired computed-TABLE views used). Because it is a real
1802
+ * column it is queryable / filterable / sortable and appears in `SELECT *`.
1803
+ * Deterministic kinds recompute on write (bounded, dep-gated); AI kinds fill
1804
+ * asynchronously. Compiled + wired by {@link file://./schema/computed-field.ts}.
1805
+ */
1806
+ computedFields?: Record<string, ComputedFieldDef>;
1487
1807
  /**
1488
1808
  * Mark this table as a *connected data type* (4.3+) — its rows are ingested
1489
1809
  * from an external system through a connector rather than authored locally.
@@ -1512,6 +1832,25 @@ interface FtsConfig {
1512
1832
  /** Columns to index. Omit to auto-detect the table's text columns. */
1513
1833
  fields?: string[];
1514
1834
  }
1835
+ /**
1836
+ * Native ANN index build tuning (pgvector HNSW). Defaults match pgvector's own,
1837
+ * so omitting this builds exactly as before. Has no effect on the in-process scan
1838
+ * or on sqlite-vec.
1839
+ */
1840
+ interface VectorIndexOptions {
1841
+ /** HNSW graph degree `m` (pgvector default 16). Higher → better recall, more memory + slower build. */
1842
+ m?: number;
1843
+ /** HNSW build candidate-list size `ef_construction` (pgvector default 64). Higher → better recall, slower build. */
1844
+ efConstruction?: number;
1845
+ /**
1846
+ * Index storage precision. `'halfvec'` (pgvector ≥ 0.7) stores the derived index
1847
+ * at 16-bit half precision — roughly halving its memory at a small recall cost —
1848
+ * while the embeddings store stays full precision, so the scan fallback remains
1849
+ * exact. Default `'none'` (full-precision `vector`). pgvector only; ignored on
1850
+ * sqlite-vec.
1851
+ */
1852
+ quantization?: 'none' | 'halfvec';
1853
+ }
1515
1854
  /**
1516
1855
  * Configuration for embedding-based semantic search on a table.
1517
1856
  */
@@ -1554,6 +1893,12 @@ interface EmbeddingsConfig {
1554
1893
  * so it fails loudly and tells you to add a pgvector index or raise the cap.
1555
1894
  */
1556
1895
  maxScanChunks?: number;
1896
+ /**
1897
+ * Optional native-index build tuning (pgvector HNSW `m` / `ef_construction`),
1898
+ * applied by `buildVectorIndex`. Omit for pgvector's defaults. No effect on the
1899
+ * in-process scan or sqlite-vec. Backward compatible — omitting it builds as before.
1900
+ */
1901
+ index?: VectorIndexOptions;
1557
1902
  }
1558
1903
  /**
1559
1904
  * Options for `Lattice.search()`.
@@ -1580,6 +1925,12 @@ interface SearchOptions {
1580
1925
  * to `topK`. Defaults to `max(topK * 4, 20)`. Ignored when no reranker is set.
1581
1926
  */
1582
1927
  rerankPoolSize?: number;
1928
+ /**
1929
+ * Query-time HNSW search breadth (pgvector `hnsw.ef_search`) for the indexed
1930
+ * vector arm. Higher trades latency for recall. Omit for pgvector's default. No
1931
+ * effect without a native index (the in-process scan is already exact).
1932
+ */
1933
+ efSearch?: number;
1583
1934
  }
1584
1935
  /**
1585
1936
  * A single search result returned by `Lattice.search()`.
@@ -1781,6 +2132,14 @@ interface CountOptions {
1781
2132
  /** Advanced filter clauses (same as QueryOptions.filters) */
1782
2133
  filters?: FilterExpr[];
1783
2134
  }
2135
+ interface BoundedCountOptions extends CountOptions {
2136
+ /**
2137
+ * Stop counting after this many matching rows (default 1000). The query scans at
2138
+ * most `cap + 1` rows, so it never becomes an O(table) COUNT on a huge table;
2139
+ * callers render a result greater than `cap` as an approximate "cap+".
2140
+ */
2141
+ cap?: number;
2142
+ }
1784
2143
  /** SQL aggregate function. */
1785
2144
  type AggregateFunction = 'count' | 'sum' | 'avg' | 'min' | 'max';
1786
2145
  /** One aggregate column in an {@link AggregateOptions}. */
@@ -1914,6 +2273,9 @@ interface SeedResult {
1914
2273
  upserted: number;
1915
2274
  linked: number;
1916
2275
  softDeleted: number;
2276
+ /** Records skipped because they had no natural-key value to identity-match on (so they
2277
+ * couldn't be upserted). 0 when every record had a key. Surfaced, never silently dropped. */
2278
+ skipped: number;
1917
2279
  /**
1918
2280
  * Links whose target row did not resolve. Empty when every link
1919
2281
  * resolved. Always present, so callers can check
@@ -2623,7 +2985,7 @@ declare class EmbeddingScanTooLargeError extends Error {
2623
2985
  * chunk vectors. Either way results respect `deleted_at IS NULL` on the base
2624
2986
  * table and are de-duplicated to the best-scoring chunk per row.
2625
2987
  */
2626
- declare function searchByEmbedding(adapter: StorageAdapter, table: string, queryText: string, config: EmbeddingsConfig, topK: number, minScore: number, pkColumn?: string): Promise<SearchResult[]>;
2988
+ declare function searchByEmbedding(adapter: StorageAdapter, table: string, queryText: string, config: EmbeddingsConfig, topK: number, minScore: number, pkColumn?: string, isCloudMember?: boolean, efSearch?: number): Promise<SearchResult[]>;
2627
2989
  interface RefreshEmbeddingsOptions {
2628
2990
  /** Only re-embed rows whose stored model differs from `config.modelId`. */
2629
2991
  staleModelOnly?: boolean;
@@ -2747,6 +3109,14 @@ interface HybridSearchOptions {
2747
3109
  ranking?: RankingOptions;
2748
3110
  /** Optional reranker over the fused top candidates (graceful fallback). */
2749
3111
  reranker?: RerankerFn;
3112
+ /**
3113
+ * Caller is a scoped cloud member (no grant on the internal embeddings store /
3114
+ * vector index). Routes the vector arm and row materialization through the
3115
+ * member-safe, row-visibility-filtered paths. Default false (owner / local).
3116
+ */
3117
+ isCloudMember?: boolean;
3118
+ /** Query-time HNSW search breadth (pgvector `hnsw.ef_search`) for the vector arm. */
3119
+ efSearch?: number;
2750
3120
  }
2751
3121
  /** Per-result score breakdown (the `--explain` payload). */
2752
3122
  interface HybridScoreBreakdown {
@@ -2910,18 +3280,569 @@ declare function graphAdjacencyBoost<T extends {
2910
3280
  }>(adapter: StorageAdapter, results: T[], opts: GraphBoostOptions): Promise<GraphBoostResult<T>[]>;
2911
3281
 
2912
3282
  /**
2913
- * Seamless cloud file-byte access an in-database presigned-URL broker.
2914
- *
2915
- * After joining a cloud via invite, a scoped member connects directly to the
2916
- * cloud's Postgres as their own least-privilege role. They can SELECT a `files`
2917
- * row they're allowed to see, but they hold no S3 credential, so fetching the
2918
- * bytes would otherwise fail. This installs a `SECURITY DEFINER` function that,
2919
- * **inside Postgres** (the only place the owner's key lives, away from members),
2920
- * gates on the member's row-visibility and computes a short-lived AWS SigV4
2921
- * presigned URL for exactly that object — so the member fetches/uploads bytes
2922
- * with zero config and never holds a key.
2923
- *
2924
- * Why sign in plpgsql: the secret must never leave the database for a member's
3283
+ * Sandboxed calculation-expression language for computed-table `calc` fields.
3284
+ *
3285
+ * A tokenizer + recursive-descent parser produce a typed AST; SQL is emitted
3286
+ * by re-serializing that AST. Raw user text NEVER reaches the SQL string —
3287
+ * this module is the injection boundary for computed tables. Anything outside
3288
+ * the grammar (semicolons, comment sequences, subqueries, table names, bind
3289
+ * parameters, double quotes, unknown functions) has no token or production,
3290
+ * so it fails with a parse error instead of ever appearing in SQL.
3291
+ *
3292
+ * Supported grammar (precedence, loosest tightest):
3293
+ *
3294
+ * OR
3295
+ * AND
3296
+ * NOT <expr>
3297
+ * comparison: `= != <> < <= > >=`, `IS [NOT] NULL`, `[NOT] LIKE`,
3298
+ * `[NOT] IN (<literal list>)`, `[NOT] BETWEEN a AND b`
3299
+ * `||` (string concatenation)
3300
+ * `+ -`
3301
+ * `* / %`
3302
+ * unary `-`
3303
+ * primary: literals, column refs, allowlisted functions, CAST, CASE, `( )`
3304
+ *
3305
+ * Literals: numeric (`12`, `3.5`), `'string'` (with `''` as the only escape,
3306
+ * re-escaped on emit), `NULL`, `TRUE`/`FALSE`. Column refs are a bare
3307
+ * identifier or a dotted belongsTo path, validated at parse time by a
3308
+ * caller-supplied resolver; at emit time the compiler maps each path to a
3309
+ * `"alias"."column"` pair, so identifiers in the output are always
3310
+ * compiler-produced. Emitted expressions are fully parenthesized, so the
3311
+ * grammar's precedence is explicit in the SQL and identical on both dialects.
3312
+ *
3313
+ * Pure module: no I/O, no imports beyond types.
3314
+ */
3315
+ type CalcDialect = 'sqlite' | 'postgres';
3316
+ /** Target types accepted by `CAST(expr AS <type>)`. */
3317
+ type CalcCastType = 'TEXT' | 'INTEGER' | 'REAL';
3318
+ /** Binary operators preserved in the AST (comparison `!=` normalizes to `<>`). */
3319
+ type BinaryOp = '+' | '-' | '*' | '/' | '%' | '||' | '=' | '<>' | '<' | '<=' | '>' | '>=';
3320
+ /** Typed AST. Only these shapes exist — SQL is produced exclusively from them. */
3321
+ type CalcNode = {
3322
+ t: 'num';
3323
+ lexeme: string;
3324
+ } | {
3325
+ t: 'str';
3326
+ value: string;
3327
+ } | {
3328
+ t: 'null';
3329
+ } | {
3330
+ t: 'bool';
3331
+ value: boolean;
3332
+ } | {
3333
+ t: 'col';
3334
+ path: readonly string[];
3335
+ } | {
3336
+ t: 'bin';
3337
+ op: BinaryOp;
3338
+ left: CalcNode;
3339
+ right: CalcNode;
3340
+ } | {
3341
+ t: 'logic';
3342
+ op: 'AND' | 'OR';
3343
+ left: CalcNode;
3344
+ right: CalcNode;
3345
+ } | {
3346
+ t: 'not';
3347
+ expr: CalcNode;
3348
+ } | {
3349
+ t: 'neg';
3350
+ expr: CalcNode;
3351
+ } | {
3352
+ t: 'isnull';
3353
+ expr: CalcNode;
3354
+ negated: boolean;
3355
+ } | {
3356
+ t: 'like';
3357
+ expr: CalcNode;
3358
+ pattern: CalcNode;
3359
+ negated: boolean;
3360
+ } | {
3361
+ t: 'in';
3362
+ expr: CalcNode;
3363
+ items: readonly CalcNode[];
3364
+ negated: boolean;
3365
+ } | {
3366
+ t: 'between';
3367
+ expr: CalcNode;
3368
+ low: CalcNode;
3369
+ high: CalcNode;
3370
+ negated: boolean;
3371
+ } | {
3372
+ t: 'case';
3373
+ whens: readonly {
3374
+ when: CalcNode;
3375
+ then: CalcNode;
3376
+ }[];
3377
+ elseExpr: CalcNode | null;
3378
+ } | {
3379
+ t: 'fn';
3380
+ name: string;
3381
+ args: readonly CalcNode[];
3382
+ } | {
3383
+ t: 'cast';
3384
+ expr: CalcNode;
3385
+ to: CalcCastType;
3386
+ };
3387
+ /** A parsed expression plus the unique column paths it references. */
3388
+ interface CalcExpr {
3389
+ readonly ast: CalcNode;
3390
+ /** Unique referenced column paths, in first-appearance order. */
3391
+ readonly columnPaths: readonly (readonly string[])[];
3392
+ }
3393
+ /**
3394
+ * Column-reference resolver supplied by the caller. Receives the dotted path
3395
+ * split into segments; returns true when the path resolves (a base column, or
3396
+ * a belongsTo chain ending in a column). An unresolved path is a parse error.
3397
+ */
3398
+ type CalcRefResolver = (path: readonly string[]) => boolean;
3399
+ /** Thrown for any lexical or syntactic violation of the expression grammar. */
3400
+ declare class CalcExprError extends Error {
3401
+ readonly position: number;
3402
+ constructor(message: string, position: number);
3403
+ }
3404
+ /**
3405
+ * Parse a calculation expression, validating every column reference against
3406
+ * `resolveRef`. Throws {@link CalcExprError} on any grammar violation or
3407
+ * unresolved reference.
3408
+ */
3409
+ declare function parseCalcExpr(source: string, resolveRef: CalcRefResolver): CalcExpr;
3410
+ /** Emit-time context: the dialect and the compiler's path → SQL mapping. */
3411
+ interface CalcEmitContext {
3412
+ dialect: CalcDialect;
3413
+ /** Map a resolved column path to its compiled `"alias"."column"` SQL. */
3414
+ columnSql: (path: readonly string[]) => string;
3415
+ }
3416
+ /**
3417
+ * Serialize a parsed expression to SQL. Every composite node is
3418
+ * parenthesized, so the emitted precedence is explicit and dialect-neutral.
3419
+ */
3420
+ declare function emitCalcExpr(expr: CalcExpr, ctx: CalcEmitContext): string;
3421
+
3422
+ /**
3423
+ * Compiler for serializable COMPUTED COLUMNS (#10) — the per-entity replacement for the
3424
+ * read-only computed-TABLE views. A computed field (`LatticeFieldDef.computed`) is a real,
3425
+ * materialized column whose value is derived by one of five kinds
3426
+ * (alias / calc / ai_classify / ai_transform / aggregate). This module compiles a
3427
+ * DETERMINISTIC field (alias / calc) into a bounded SQL scalar expression the runtime
3428
+ * recomputes on write via a single targeted `UPDATE … WHERE pk = ?` (one row).
3429
+ *
3430
+ * The SQL is emitted through the SAME `emitCalcExpr` the retired view compiler used, so
3431
+ * there is one source of truth for calc semantics (no JS/SQL divergence). Non-deterministic
3432
+ * or non-same-row kinds are returned as DEFERRED — the field is valid and registered, but
3433
+ * its value is produced by a later mechanism (the AI fill engine for ai_* kinds; the
3434
+ * aggregate/belongsTo-path recompute) rather than the synchronous write-path UPDATE.
3435
+ */
3436
+
3437
+ /** Why a computed field is not recomputed by the synchronous same-row UPDATE path. */
3438
+ type DeferredReason = 'ai' | 'aggregate' | 'path';
3439
+ /** AI-fill recompute metadata, carried so the runtime never re-parses the def. */
3440
+ interface AiFieldPlan {
3441
+ kind: 'classify' | 'transform';
3442
+ /** Same-row columns fed to the model — a write to any of these invalidates the cell. */
3443
+ inputs: string[];
3444
+ prompt: string;
3445
+ /** Allowed labels (classify only). */
3446
+ labels?: string[];
3447
+ model: 'default' | 'cheapest';
3448
+ }
3449
+ /** Aggregate recompute metadata, carried so the runtime never re-parses the def. */
3450
+ interface AggregateFieldPlan {
3451
+ /** Junction/child table folded into one scalar per base row. */
3452
+ junction: string;
3453
+ /** Remote relation/column segment of `via` (`'<junction>.<remote>'`). */
3454
+ remote: string;
3455
+ fn: 'count' | 'sum' | 'avg' | 'min' | 'max' | 'concat';
3456
+ /** Remote column to aggregate (required for every fn except count). */
3457
+ column?: string;
3458
+ }
3459
+ interface CompiledComputedField {
3460
+ /** The physical column the value materializes into. */
3461
+ column: string;
3462
+ /** SQL scalar expression over the base row's OWN columns, for the recompute UPDATE.
3463
+ * Empty when {@link deferred} is set. */
3464
+ sql: string;
3465
+ /** Same-row columns this value depends on (drives dep-gated recompute). Empty for
3466
+ * aggregate/path; for a deferred `ai` field these ARE populated (the AI cell is
3467
+ * invalidated when an input column changes). */
3468
+ deps: string[];
3469
+ /** Set when the value is produced by a later mechanism, not the same-row UPDATE. */
3470
+ deferred?: DeferredReason;
3471
+ /** Present iff `deferred === 'ai'` — drives the async fill + staleness. */
3472
+ ai?: AiFieldPlan;
3473
+ /** Present iff `deferred === 'aggregate'` — drives the correlated-subquery recompute. */
3474
+ aggregate?: AggregateFieldPlan;
3475
+ }
3476
+
3477
+ /**
3478
+ * Computed-table compiler + registration.
3479
+ *
3480
+ * A computed table is a live, READ-ONLY SQL VIEW over one base table,
3481
+ * declared in the workspace config (`computed:`). This module compiles a
3482
+ * {@link ComputedTableDef} into dialect-specific DDL:
3483
+ *
3484
+ * - The view projects the base primary key `AS "id"` first, then each field
3485
+ * in declaration order.
3486
+ * - Soft-deleted base rows are filtered (`"b"."deleted_at" IS NULL` when the
3487
+ * base carries that column). Each unique belongsTo path prefix becomes ONE
3488
+ * LEFT JOIN with a deterministic alias (`j1`, `j2`, … assigned in
3489
+ * sorted-path order, so recompiles are byte-stable); a joined table's
3490
+ * `deleted_at IS NULL` lives in the JOIN's ON clause, so an invisible
3491
+ * parent nulls the field without dropping the base row.
3492
+ * - `aggregate` fields compile to correlated subqueries over the junction.
3493
+ * - AI fields LEFT JOIN the `__lattice_ai_map` / `__lattice_ai_cell`
3494
+ * bookkeeping tables (see `computed-fill.ts`) — reads never invoke a model.
3495
+ * A transform's join matches on an `input_key` computed in SQL from the
3496
+ * declared inputs IN ORDER, so any source change makes the join miss and
3497
+ * the field reads NULL until the next fill pass (never stale).
3498
+ * - With `cloud.rowVisible`, every relation gains the same
3499
+ * `lattice_row_visible(...)` predicate the cell-masking views use, so a
3500
+ * scoped member's reads stay row-filtered.
3501
+ *
3502
+ * Everything interpolated into SQL is either a validated bare identifier
3503
+ * (see `identifier.ts`), an escaped string literal produced here, or the
3504
+ * re-serialized output of the sandboxed expression parser (`calc-expr.ts`).
3505
+ * Raw config text never reaches the SQL string.
3506
+ */
3507
+
3508
+ /** What the compiler needs to know about one referencable table. */
3509
+ interface ComputedSchemaTable {
3510
+ /** Column names present on the table. */
3511
+ columns: ReadonlySet<string>;
3512
+ /** Declared belongsTo relations, keyed by relation name. */
3513
+ relations: Readonly<Record<string, BelongsToRelation>>;
3514
+ /** Normalized primary-key columns. */
3515
+ primaryKey: readonly string[];
3516
+ /** Whether the table carries the soft-delete `deleted_at` column. */
3517
+ hasDeletedAt: boolean;
3518
+ /** Canonical Lattice field types, when known (display metadata only). */
3519
+ fieldTypes?: Readonly<Record<string, string>>;
3520
+ }
3521
+ /** table name → shape; built from the parsed config / live schema registry. */
3522
+ type ComputedSchema = ReadonlyMap<string, ComputedSchemaTable>;
3523
+ /** One compiled AI-derived field, including the SQL its fill pass runs. */
3524
+ interface CompiledAiField {
3525
+ /** Cache key: `<table>.<field>`. */
3526
+ key: string;
3527
+ /** Bare field name. */
3528
+ field: string;
3529
+ kind: 'ai_classify' | 'ai_transform';
3530
+ /** Declared input paths, in config order (order is part of the cache identity). */
3531
+ inputs: readonly string[];
3532
+ /** Compiled SQL expression per input, aligned with `inputs`. */
3533
+ inputSql: readonly string[];
3534
+ /** ai_transform only: the SQL `input_key` expression the view joins on. */
3535
+ inputKeySql?: string;
3536
+ /** ai_transform only: `CAST("b"."<pk>" AS TEXT)`. */
3537
+ rowIdSql?: string;
3538
+ /**
3539
+ * SELECT returning this field's unfilled work (no LIMIT — the fill engine
3540
+ * appends one). Classifier: never-seen DISTINCT input values. Transform:
3541
+ * join-miss rows with `row_id`, the SQL-computed `input_key`, and each
3542
+ * input value as `input_<i>` — the fill engine reads the key from the
3543
+ * database so it can never disagree with the view's join expression.
3544
+ */
3545
+ pendingSql: string;
3546
+ prompt: string;
3547
+ labels?: readonly string[];
3548
+ model: 'default' | 'cheapest';
3549
+ }
3550
+ interface CompiledComputedTable {
3551
+ viewName: string;
3552
+ /** SELECT body (a preview runs it with LIMIT). */
3553
+ selectSql: string;
3554
+ /** `DROP VIEW IF EXISTS` + `CREATE VIEW`, dialect-specific. */
3555
+ createSql: string;
3556
+ /** `['id', ...fieldNames]` in declaration order. */
3557
+ columns: string[];
3558
+ /** Field → canonical Lattice display type (includes the projected `id`). */
3559
+ fieldTypes: Record<string, string>;
3560
+ /** Base + every joined / junction / remote table (bookkeeping tables excluded). */
3561
+ sources: string[];
3562
+ aiFields: CompiledAiField[];
3563
+ /** Stable hash of `createSql` — the idempotence guard for Postgres DDL. */
3564
+ contentHash: string;
3565
+ }
3566
+ /** Thrown by {@link computedTableOrder} when computed→computed bases form a cycle. */
3567
+ declare class ComputedTableCycleError extends Error {
3568
+ readonly cycle: string[];
3569
+ constructor(cycle: string[]);
3570
+ }
3571
+ /**
3572
+ * Cloud-compile options. On a secured team cloud a Postgres view executes with
3573
+ * its OWNER's rights, so a computed view must (a) row-filter per viewer with
3574
+ * `lattice_row_visible(...)` predicates and (b) read every source column THROUGH
3575
+ * that table's cell-masking view when one exists — otherwise the computed view
3576
+ * exposes the RAW value of a column the owner masked from this member's role, a
3577
+ * column-level cross-tenant leak that the normal `<t>_v` masked read path would
3578
+ * have caught.
3579
+ */
3580
+ interface CloudCompileOptions {
3581
+ rowVisible: true;
3582
+ /**
3583
+ * Source tables that carry a cell-masking view (`<t>_v`, generated from
3584
+ * `__lattice_column_policy`; see cloud/audience.ts). A computed field reading a
3585
+ * column of such a table is compiled to read it through `<t>_v`, which encodes
3586
+ * BOTH per-column masking (a masked cell reads NULL for a member) AND row
3587
+ * visibility — so a table read through its masking view needs no separate
3588
+ * `lattice_row_visible` predicate (the view already applies it).
3589
+ */
3590
+ maskedTables?: ReadonlySet<string>;
3591
+ }
3592
+ /**
3593
+ * Order computed-table definitions so every base compiles before the tables
3594
+ * built on it (dependencies first). Only bases that are themselves computed
3595
+ * tables participate; entity bases are leaves. Throws
3596
+ * {@link ComputedTableCycleError} on a cycle, naming it.
3597
+ */
3598
+ declare function computedTableOrder(defs: Record<string, ComputedTableDef>): string[];
3599
+ /**
3600
+ * Compile one computed-table definition into dialect-specific view DDL plus
3601
+ * the metadata later slices need (columns, display types, AI fill SQL).
3602
+ * Pure: validates everything it touches and throws a descriptive error on
3603
+ * any unknown table, relation, or column. `schema` must already contain any
3604
+ * computed table used as `base` (compile in {@link computedTableOrder}).
3605
+ */
3606
+ declare function compileComputedTable(name: string, def: ComputedTableDef, schema: ComputedSchema, dialect: 'sqlite' | 'postgres', cloud?: CloudCompileOptions): CompiledComputedTable;
3607
+ /**
3608
+ * The registration seam the core exposes to this module — kept structural so
3609
+ * `lattice.ts` can pass a closure-backed host without a runtime import cycle.
3610
+ */
3611
+ interface ComputedTableHost {
3612
+ adapter: StorageAdapter;
3613
+ /** Apply version-guarded migrations (the Postgres DDL path). */
3614
+ migrate(migrations: Migration[]): Promise<void>;
3615
+ introspectColumns(table: string): Promise<string[]>;
3616
+ /**
3617
+ * Batch-introspect several tables' columns in ONE round-trip (table → ordered
3618
+ * columns). Optional: when absent, registration falls back to a per-table
3619
+ * {@link introspectColumns} loop. Providing it collapses the post-create
3620
+ * introspection of K computed views into a single `information_schema` query,
3621
+ * so a pooled-cloud open never pays K serial round-trips just to read columns.
3622
+ */
3623
+ introspectAllColumns?(tables: string[]): Promise<Map<string, Set<string>>>;
3624
+ /** Register the already-existing view in the live schema registry. Issues NO DDL. */
3625
+ register(table: string, def: TableDefinition, columns: readonly string[]): void;
3626
+ }
3627
+ interface RegisterComputedTablesOptions {
3628
+ /** Lookup for every referencable table (entities + already-registered tables). */
3629
+ schema: ComputedSchema;
3630
+ dialect: 'sqlite' | 'postgres';
3631
+ /**
3632
+ * Scoped cloud member open: the role cannot DDL, so register purely by
3633
+ * introspection — no view DDL, no bookkeeping-table creation, and a view
3634
+ * this member cannot see is skipped rather than treated as a failure.
3635
+ */
3636
+ introspectOnly?: boolean;
3637
+ /**
3638
+ * Cloud compile: emit per-relation `lattice_row_visible` predicates and read
3639
+ * masked source tables through their cell-masking `<t>_v` view (see
3640
+ * {@link CloudCompileOptions}).
3641
+ */
3642
+ cloud?: CloudCompileOptions;
3643
+ /**
3644
+ * Runtime (ops-layer) registration: execute the view DDL directly (drop +
3645
+ * recreate) on Postgres too, instead of the content-hash migration the open
3646
+ * path uses. A runtime edit can be REVERTED to a prior definition whose
3647
+ * content hash was already applied once — a version-guarded migration would
3648
+ * skip that DDL and silently leave the view on the newer definition.
3649
+ */
3650
+ directDdl?: boolean;
3651
+ }
3652
+ interface ComputedRegistrationResult {
3653
+ /** Successfully compiled + registered computed tables, in topo order. */
3654
+ registered: string[];
3655
+ /** Introspect-only opens where the view is not (yet) visible to this role. */
3656
+ skipped: string[];
3657
+ /** Per-table failures. A failure never aborts the open or the other tables. */
3658
+ errors: {
3659
+ table: string;
3660
+ error: string;
3661
+ }[];
3662
+ /** Compiled artifacts, for the fill engine / preview / ops layers. */
3663
+ compiled: Map<string, CompiledComputedTable>;
3664
+ }
3665
+ /**
3666
+ * Compile every computed-table definition in topological order, execute the
3667
+ * view DDL, introspect the result, and register it with the host.
3668
+ *
3669
+ * DDL is planned dependency-aware: when a table's view must be (re)created —
3670
+ * SQLite and the runtime `directDdl` path always recreate; Postgres recreates
3671
+ * when the definition's content-hash migration has not been applied — every
3672
+ * TRANSITIVE dependent (a computed table based on it) is dropped first in
3673
+ * reverse-topological order and force-recreated afterwards in topological
3674
+ * order. Postgres refuses `DROP VIEW` while dependents exist, so without the
3675
+ * plan a changed base failed to re-register AND took its dependents with it
3676
+ * on the next open; and a dependent whose own hash is unchanged must be
3677
+ * recreated DIRECTLY (its migration version is already recorded — a
3678
+ * version-guarded migrate would skip the CREATE and leave the view missing).
3679
+ * (`CREATE OR REPLACE VIEW` is deliberately not used — it fails whenever the
3680
+ * column list changes.)
3681
+ *
3682
+ * A definition that fails to compile (or whose DDL fails) must NOT brick the
3683
+ * open: it is skipped, recorded under field `'*'` in
3684
+ * `__lattice_computed_state`, reported in the returned result, and the
3685
+ * remaining tables continue. This function itself never throws for a
3686
+ * per-table failure.
3687
+ *
3688
+ * After registration, stored AI outputs whose `prompt_hash` no longer matches
3689
+ * the current definition are purged ({@link purgeStaleAiFields}), so a
3690
+ * definition changed through ANY path (ops layer, hand-edited YAML,
3691
+ * member-hydrated config) never keeps serving values derived from a prompt or
3692
+ * label set that no longer exists.
3693
+ */
3694
+ declare function registerComputedTables(host: ComputedTableHost, defs: Record<string, ComputedTableDef>, opts: RegisterComputedTablesOptions): Promise<ComputedRegistrationResult>;
3695
+
3696
+ /**
3697
+ * AI-fill engine for computed tables.
3698
+ *
3699
+ * AI-derived fields never re-run a model at read time: model outputs are
3700
+ * materialized once into shared bookkeeping tables that the computed-table
3701
+ * view LEFT JOINs, so reads are always deterministic SQL.
3702
+ *
3703
+ * Three unregistered `__lattice_` bookkeeping tables (raw DDL, dialect-neutral,
3704
+ * no SQL-side defaults — every writer supplies explicit ISO timestamps, keeping
3705
+ * the CREATE byte-identical across dialects):
3706
+ *
3707
+ * - `__lattice_ai_map` — classifier memo: one row per (field, distinct input
3708
+ * value). `label` is NULLABLE: NULL records "the model declined", kept so the
3709
+ * same value is never re-asked. The view joins on the CAST-to-TEXT input.
3710
+ * - `__lattice_ai_cell` — per-row transform output, keyed (field, row). The
3711
+ * `input_key` column stores the SQL-computed concatenation of the field's
3712
+ * inputs; the view's join also matches on it, so a changed source row makes
3713
+ * the join miss and the field reads NULL until the next fill (never stale).
3714
+ * - `__lattice_computed_state` — per-(table, field) fill status for progress
3715
+ * and error reporting. Registration failures land here under field `'*'`.
3716
+ *
3717
+ * The LLM is INJECTED via the minimal {@link FillLlm} interface — this module
3718
+ * performs no model calls of its own and the schema layer stays free of any
3719
+ * client dependency. The `model` string passed through is the field's declared
3720
+ * tier (`'default'` / `'cheapest'`); the adapter maps it to a real model id.
3721
+ */
3722
+
3723
+ declare const AI_MAP_TABLE = "__lattice_ai_map";
3724
+ declare const AI_CELL_TABLE = "__lattice_ai_cell";
3725
+ declare const COMPUTED_STATE_TABLE = "__lattice_computed_state";
3726
+ /**
3727
+ * Minimal LLM interface the fill engine depends on. Callers adapt their own
3728
+ * client: `model` arrives as the field's declared tier ('default'/'cheapest');
3729
+ * the implementation resolves it to a concrete model. The returned string is
3730
+ * the raw completion text.
3731
+ */
3732
+ interface FillLlm {
3733
+ complete(opts: {
3734
+ system: string;
3735
+ user: string;
3736
+ model: string;
3737
+ }): Promise<string>;
3738
+ }
3739
+ interface ComputedFillOptions {
3740
+ /** Pending items fetched (and classifier values sent) per model call. Default 50. */
3741
+ batchSize?: number;
3742
+ }
3743
+ /** Per-field outcome of a fill run. */
3744
+ interface FieldFillResult {
3745
+ /** Bare field name. */
3746
+ field: string;
3747
+ /** Cache key: `<table>.<field>`. */
3748
+ key: string;
3749
+ kind: 'ai_classify' | 'ai_transform';
3750
+ /** 'idle' when the field converged; 'error' when it stopped early. */
3751
+ status: 'idle' | 'error';
3752
+ /** Rows/values materialized by THIS run. */
3753
+ filled: number;
3754
+ /** Items still unfilled after the run (0 unless the field errored). */
3755
+ pending: number;
3756
+ error?: string;
3757
+ }
3758
+ /** Report returned by {@link runComputedFill}. Errors are returned, never thrown away. */
3759
+ interface ComputedFillReport {
3760
+ table: string;
3761
+ fields: FieldFillResult[];
3762
+ }
3763
+ /** A row of `__lattice_computed_state`. */
3764
+ interface ComputedFieldState {
3765
+ table_name: string;
3766
+ field: string;
3767
+ status: 'idle' | 'running' | 'error';
3768
+ error: string | null;
3769
+ pending: number | null;
3770
+ filled: number | null;
3771
+ started_at: string | null;
3772
+ finished_at: string | null;
3773
+ }
3774
+ /**
3775
+ * Create the three bookkeeping tables if absent. Idempotent; safe on both
3776
+ * dialects (plain TEXT/INTEGER columns, composite PKs, no defaults).
3777
+ */
3778
+ declare function ensureAiTables(adapter: StorageAdapter): Promise<void>;
3779
+ /** Read the fill/error state rows for one computed table. */
3780
+ declare function readComputedState(adapter: StorageAdapter, tableName: string): Promise<ComputedFieldState[]>;
3781
+ /**
3782
+ * Delete every materialized output + state row for one field. Called when the
3783
+ * field's DEFINITION changes (prompt, labels, model, inputs) — the cached
3784
+ * outputs no longer describe the field, so they are purged and the next fill
3785
+ * re-derives them. `fieldKey` is `'<table>.<field>'`.
3786
+ */
3787
+ declare function purgeAiField(adapter: StorageAdapter, fieldKey: string): Promise<void>;
3788
+ /**
3789
+ * Run the fill pass for every AI field of a compiled computed table.
3790
+ *
3791
+ * - `ai_classify`: only never-seen DISTINCT input values reach the model, in
3792
+ * batches (~50 per call), as one strict-JSON request mapping each value to a
3793
+ * label. Every returned label is validated against the allowed set; a value
3794
+ * with an out-of-set (or missing) label is recorded as an error for the
3795
+ * field and NEVER stored as a label. A `null` label means the model
3796
+ * declined — stored so the value is not re-asked.
3797
+ * - `ai_transform`: one model call per join-miss row; the row's `input_key`
3798
+ * is SELECTed from the database via the same SQL expression the view joins
3799
+ * on, so the two can never disagree.
3800
+ *
3801
+ * An LLM/parse error records status `'error'` for that field, stops that
3802
+ * field's remaining batches (already-written rows stay), continues the other
3803
+ * fields, and is RETURNED in the report — unfilled rows keep reading NULL via
3804
+ * the view's join miss, never a stale value. This function only throws for
3805
+ * infrastructure failures (e.g. the database itself is unreachable).
3806
+ */
3807
+ declare function runComputedFill(adapter: StorageAdapter, llm: FillLlm, compiled: CompiledComputedTable, opts?: ComputedFillOptions): Promise<ComputedFillReport>;
3808
+
3809
+ /**
3810
+ * AI fill for #10 computed COLUMNS (the per-entity replacement for the retired
3811
+ * computed-TABLE AI fields). An `ai_classify` / `ai_transform` computed field is a
3812
+ * real column that starts NULL and is populated asynchronously by a model. This
3813
+ * engine scans a bounded window of un-filled cells (`WHERE "<col>" IS NULL`), calls
3814
+ * the injected {@link FillLlm} per row, validates the output, and writes it back with
3815
+ * a single targeted `UPDATE … WHERE pk` (bounded, never a whole-table scan).
3816
+ *
3817
+ * It writes through the raw adapter (NOT `Lattice.update`) so a fill does not re-fire
3818
+ * the write hooks that trigger it — that would loop forever. The staleness contract
3819
+ * ("a changed input NULLs the cell before the refill") is enforced in the write path
3820
+ * (`Lattice._nullStaleAiColumns`); this engine only ever FILLS a NULL cell, so it is
3821
+ * safe to run repeatedly and idempotent once every cell is populated.
3822
+ */
3823
+
3824
+ interface FieldFillReport {
3825
+ /** Cells populated this run. */
3826
+ filled: number;
3827
+ /** Cells whose model output was rejected/failed (left NULL). */
3828
+ failed: number;
3829
+ /** First few human-readable failure reasons (never secrets). */
3830
+ errors: string[];
3831
+ }
3832
+
3833
+ /**
3834
+ * Seamless cloud file-byte access — an in-database presigned-URL broker.
3835
+ *
3836
+ * After joining a cloud via invite, a scoped member connects directly to the
3837
+ * cloud's Postgres as their own least-privilege role. They can SELECT a `files`
3838
+ * row they're allowed to see, but they hold no S3 credential, so fetching the
3839
+ * bytes would otherwise fail. This installs a `SECURITY DEFINER` function that,
3840
+ * **inside Postgres** (the only place the owner's key lives, away from members),
3841
+ * gates on the member's row-visibility and computes a short-lived AWS SigV4
3842
+ * presigned URL for exactly that object — so the member fetches/uploads bytes
3843
+ * with zero config and never holds a key.
3844
+ *
3845
+ * Why sign in plpgsql: the secret must never leave the database for a member's
2925
3846
  * process, so a Node-side presign is out — the signature is computed in-DB via
2926
3847
  * `pgcrypto` HMAC-SHA256. Correctness of the SigV4 chain is verified against
2927
3848
  * AWS's published test vectors (no real S3 needed).
@@ -3303,6 +4224,21 @@ type EventHandler<T> = (data: T) => void;
3303
4224
  type PkLookup = string | Record<string, unknown>;
3304
4225
  declare class Lattice {
3305
4226
  private readonly _adapter;
4227
+ /**
4228
+ * Ambient transaction executor for {@link transaction}. When a store is set,
4229
+ * every write helper routes through it (via {@link _exec}) instead of the base
4230
+ * adapter, so the whole call chain lands on one transaction connection. Scoped
4231
+ * to the async context of the `transaction(fn)` callback, so concurrent callers
4232
+ * never share a transaction.
4233
+ */
4234
+ private readonly _txStore;
4235
+ /**
4236
+ * Serializer for schema mutations (CREATE TABLE / ALTER … ADD COLUMN). See
4237
+ * {@link withSchemaLock}. The flag marks "already inside the lock" for reentrancy;
4238
+ * the chain is the FIFO queue that runs one locked section at a time.
4239
+ */
4240
+ private readonly _schemaLockFlag;
4241
+ private _schemaLockChain;
3306
4242
  private _changelogService?;
3307
4243
  private _changelogWriterInstance?;
3308
4244
  private _reportBuilder?;
@@ -3364,6 +4300,23 @@ declare class Lattice {
3364
4300
  private readonly _computed;
3365
4301
  /** table → materialized rollup specs (P-VIEW). */
3366
4302
  private readonly _rollups;
4303
+ /** table → compiled DETERMINISTIC computed columns (same-row alias/calc, #10) for the
4304
+ * bounded write-path recompute UPDATE. Deferred kinds (aggregate/AI/belongsTo-path)
4305
+ * are produced by their own mechanisms and are NOT in this map. */
4306
+ private readonly _computedFieldSql;
4307
+ /** All compiled computed fields per table (incl. deferred) — for introspection + fill. */
4308
+ private readonly _computedFieldPlans;
4309
+ /** table → its AI computed fields (column + input deps + plan) — the async-fill hot path. */
4310
+ private readonly _aiComputedFields;
4311
+ /** Computed-table (read-only view) definitions from the YAML config. */
4312
+ private readonly _configComputedTables;
4313
+ /**
4314
+ * Names of the computed tables registered by this instance — the single
4315
+ * source of truth the write-refusal guard and {@link isComputedTable} share.
4316
+ */
4317
+ private readonly _computedTables;
4318
+ /** Outcome of the init-time computed-table registration (see accessor). */
4319
+ private _computedRegistration;
3367
4320
  /** source table → parents whose rollup it feeds (for incremental recompute). */
3368
4321
  private readonly _rollupSources;
3369
4322
  /**
@@ -3446,6 +4399,88 @@ declare class Lattice {
3446
4399
  init(options?: InitOptions): Promise<void>;
3447
4400
  /** Async tail of init(). See {@link init} for the sync-validation phase. */
3448
4401
  private _initAsync;
4402
+ /**
4403
+ * Register the config-declared computed tables (read-only SQL views) in
4404
+ * topological order: compile, execute the view DDL (SQLite drops +
4405
+ * recreates unconditionally; Postgres guards the DDL behind a content-hash
4406
+ * migration version so a converged open issues none), introspect, and
4407
+ * register each view as a queryable table. A definition that fails to
4408
+ * compile never bricks the open — it is recorded under field `'*'` in
4409
+ * `__lattice_computed_state`, surfaced via {@link getComputedRegistration},
4410
+ * and the remaining tables continue.
4411
+ */
4412
+ private _registerConfigComputedTables;
4413
+ /**
4414
+ * The registration host over THIS instance — shared by the init-time
4415
+ * registration and {@link registerComputedTablesLive} so both register
4416
+ * through the identical seam.
4417
+ */
4418
+ private _computedTableHost;
4419
+ /**
4420
+ * On a secured team cloud, computed views must compile with per-relation
4421
+ * `lattice_row_visible(...)` predicates: a Postgres view executes with its
4422
+ * OWNER's rights, so without the predicates a member granted SELECT on the
4423
+ * view would read every base row, bypassing RLS. The predicate helper only
4424
+ * exists once the cloud RLS bootstrap has run — detected by its
4425
+ * `__lattice_owners` bookkeeping table (same tell `probeCloud` uses). A probe
4426
+ * failure propagates: silently compiling without the predicates on a cloud
4427
+ * would be a row-visibility hole, and a connection broken enough to fail this
4428
+ * one SELECT fails the open anyway.
4429
+ */
4430
+ computedCloudOption(opts?: {
4431
+ introspectOnly?: boolean;
4432
+ }): Promise<CloudCompileOptions | undefined>;
4433
+ /**
4434
+ * Table lookup for the computed-table compiler: every registered table's
4435
+ * declared + introspected columns, belongsTo relations, and normalized
4436
+ * primary key. Shared by the init-time registration and the runtime ops
4437
+ * layer (create/preview/field pickers), so the two can never disagree about
4438
+ * what a definition may reference.
4439
+ */
4440
+ computedSchemaLookup(): Map<string, ComputedSchemaTable>;
4441
+ /**
4442
+ * Register computed-table definitions at RUNTIME — the live counterpart of
4443
+ * the init-time registration, used by the GUI ops layer (create/update). It
4444
+ * runs the SAME registration path as the open (compile in topological order,
4445
+ * bookkeeping-table ensure, view DDL, introspect, live-register) with one
4446
+ * deliberate difference: the view DDL executes directly (drop + recreate)
4447
+ * rather than through the open path's content-hash migration, because a
4448
+ * runtime edit can be reverted to a PRIOR definition whose hash was already
4449
+ * applied once — a version-guarded migration would skip that DDL. Successful
4450
+ * registrations join {@link isComputedTable} / {@link getComputedTableNames}
4451
+ * (so the write-refusal guard covers them immediately) and their compiled
4452
+ * artifacts merge into {@link getComputedRegistration}. Per-definition
4453
+ * failures are RETURNED in `errors`, never thrown — the caller decides
4454
+ * whether a failure aborts its operation.
4455
+ */
4456
+ registerComputedTablesLive(defs: Record<string, ComputedTableDef>): Promise<ComputedRegistrationResult>;
4457
+ /**
4458
+ * Remove a computed table from the live registry — the inverse of
4459
+ * {@link registerComputedTablesLive} for one table. Unregisters the view
4460
+ * from the schema registry (it stops being listed/queryable) and from the
4461
+ * computed-table set (the write-refusal guard no longer names it). Issues NO
4462
+ * DDL — the caller owns dropping the view. Throws for a name that is not a
4463
+ * registered computed table, so a plain entity can never be unregistered
4464
+ * through this path.
4465
+ */
4466
+ unregisterComputedTable(name: string): void;
4467
+ /**
4468
+ * True when `name` is a computed table registered by this instance —
4469
+ * a read-only projection that refuses direct writes.
4470
+ */
4471
+ isComputedTable(name: string): boolean;
4472
+ /** Names of the computed tables registered by this instance. */
4473
+ getComputedTableNames(): string[];
4474
+ /**
4475
+ * Outcome of the init-time computed-table registration: what registered,
4476
+ * what was skipped (introspect-only member opens), per-table errors, and
4477
+ * the compiled artifacts (view SQL + AI fill queries) for downstream
4478
+ * consumers such as the fill engine. Null when the config declares no
4479
+ * computed tables or init has not run.
4480
+ */
4481
+ getComputedRegistration(): ComputedRegistrationResult | null;
4482
+ /** Refuse writes against a computed table — it is a read-only projection. */
4483
+ private _assertNotComputedTable;
3449
4484
  /**
3450
4485
  * Run additional migrations after init(). Useful for package-level schema
3451
4486
  * changes applied at runtime (e.g. update hooks that add columns).
@@ -3469,6 +4504,51 @@ declare class Lattice {
3469
4504
  * Teams schema spec → DDL translation).
3470
4505
  */
3471
4506
  getDialect(): 'sqlite' | 'postgres';
4507
+ /**
4508
+ * The adapter that write/read SQL should execute against: the ambient
4509
+ * transaction connection when inside {@link transaction}, otherwise the base
4510
+ * adapter. Only the row `run`/`get` execution helpers consult this — schema,
4511
+ * introspection, search, and graph helpers keep the base adapter, since those
4512
+ * are structural and should not be scoped to a data transaction.
4513
+ */
4514
+ private _exec;
4515
+ /**
4516
+ * Run `fn` inside a single database transaction. Every write `fn` performs
4517
+ * through this Lattice (insert / update / delete and their audit + changelog
4518
+ * writes) executes on one connection and commits together, or rolls back
4519
+ * together if `fn` throws. Reads inside `fn` see its own uncommitted writes.
4520
+ *
4521
+ * The transaction is scoped to the async context of `fn` (via
4522
+ * `AsyncLocalStorage`), so two concurrent callers on the same Lattice never
4523
+ * accidentally share a transaction. A nested `transaction` call reuses the
4524
+ * outer transaction rather than opening a second one. When the adapter cannot
4525
+ * open a transaction (`withClient` unavailable), `fn` runs WITHOUT one — the
4526
+ * caller's own validation still applies; this mirrors the hard-delete fallback.
4527
+ *
4528
+ * @since 5.0.0
4529
+ */
4530
+ transaction<T>(fn: () => Promise<T>): Promise<T>;
4531
+ /**
4532
+ * Run `fn` with exclusive access to schema mutation. Serializes CREATE TABLE /
4533
+ * ALTER … ADD COLUMN so concurrent callers can't interleave a check-then-DDL
4534
+ * across an `await` and collide. This matters because the SQLite adapter is one
4535
+ * synchronous connection: when a parallel folder ingest has two files that both
4536
+ * extract a new "Invoices" entity, or both add an "amount" column, the un-serialized
4537
+ * loser hits `CREATE TABLE`/`ADD COLUMN` after the winner already ran it and throws
4538
+ * "table already exists" / "duplicate column name". Row INSERTs are deliberately NOT
4539
+ * serialized — they're atomic auto-commit statements with uuid keys and no
4540
+ * read-modify-write, so they stay fully concurrent.
4541
+ *
4542
+ * Reentrant via `AsyncLocalStorage`: a locked section that itself triggers more
4543
+ * DDL (e.g. `createUserEntity` → `addColumn` when securing a cloud table) runs the
4544
+ * nested call inline instead of deadlocking on the queue it already holds — the same
4545
+ * ambient-context pattern as {@link transaction}. A rejected `fn` never poisons the
4546
+ * queue: the chain advances on settle either way, and the caller still sees the
4547
+ * rejection through the returned promise.
4548
+ *
4549
+ * @since 5.0.0
4550
+ */
4551
+ withSchemaLock<T>(fn: () => Promise<T>): Promise<T>;
3472
4552
  /**
3473
4553
  * True when a table opts into the observation/changelog substrate
3474
4554
  * (`def.changelog`). Callers that want to bypass the high-level {@link delete}
@@ -3608,6 +4688,13 @@ declare class Lattice {
3608
4688
  getConnectedSource(table: string): ConnectorSource | undefined;
3609
4689
  /** Names of all registered connected data types (tables with a `source`). */
3610
4690
  connectedTables(): string[];
4691
+ /**
4692
+ * Column names encrypted-at-rest for `table` (decrypted on read). Any caller that ships rows
4693
+ * OUTSIDE the process — an HTTP API response, a render, a log — must mask these; an unmasked
4694
+ * read returns cleartext credentials. Empty set when the table has no encrypted columns or the
4695
+ * encryption layer isn't built yet.
4696
+ */
4697
+ getEncryptedColumns(table: string): ReadonlySet<string>;
3611
4698
  /** Post-insert side effects (changelog, audit, write hooks, embedding sync),
3612
4699
  * identical for the plain and force-visibility insert paths. */
3613
4700
  private _afterInsert;
@@ -3629,6 +4716,50 @@ declare class Lattice {
3629
4716
  * columns, and fold them into the update payload. No-op otherwise.
3630
4717
  */
3631
4718
  private _recomputeComputedOnUpdate;
4719
+ /**
4720
+ * Recompute the DETERMINISTIC same-row computed columns (#10 alias/calc) for ONE row via
4721
+ * a single bounded `UPDATE … WHERE pk` (one row, no scan). Runs AFTER the row
4722
+ * is written, so the compiler-emitted SQL expressions read the row's own just-written
4723
+ * columns. `changedCols` = the update payload keys (dep-gated recompute); pass `null` for
4724
+ * an insert (recompute every computed column on the new row). The SET right-hand sides
4725
+ * are compiler-generated SQL over validated, quoted column names — never user input.
4726
+ */
4727
+ private _recomputeComputedFieldColumns;
4728
+ /**
4729
+ * Derive computed columns after a row is written: recompute the deterministic same-row
4730
+ * columns and NULL any AI cells whose inputs changed. Called by every write path (insert,
4731
+ * update, upsert, the natural-key upsert/enrich the sync + seed engines use) so a computed
4732
+ * field is never left unpopulated or stale regardless of HOW the row was written.
4733
+ * `changedCols === null` = a full-row write (recompute all + null every AI cell).
4734
+ */
4735
+ private _deriveComputedAfterWrite;
4736
+ /**
4737
+ * NULL the AI computed cells on ONE row whose input columns just changed (one bounded
4738
+ * row update, no scan). The "never serve stale" contract: a changed input clears the derived value
4739
+ * immediately; {@link fillComputedFields} repopulates it out-of-band. `changedCols === null`
4740
+ * NULLs every AI cell on the row (a full-row write, where any input may have changed).
4741
+ */
4742
+ private _nullStaleAiColumns;
4743
+ /**
4744
+ * The compiled computed-field plans for a table (all kinds, incl. deferred AI/aggregate) —
4745
+ * for the GUI field editor + the fill/refresh machinery. Empty when the table has none.
4746
+ */
4747
+ getComputedFieldPlans(table: string): CompiledComputedField[];
4748
+ /** Whether a table has any AI computed fields awaiting fill. */
4749
+ hasAiComputedFields(table: string): boolean;
4750
+ /**
4751
+ * Populate the un-filled (NULL) cells of a table's AI computed fields using the injected
4752
+ * {@link FillLlm}. Kept OUT of the write path (no model calls in the core DB layer) — the GUI
4753
+ * calls this fire-and-forget after a write and on open (backfill), a test injects a fake LLM.
4754
+ * Bounded (scans `WHERE col IS NULL LIMIT n`, skips tombstones). Returns a report;
4755
+ * never throws for a per-cell model failure (those are counted + surfaced in the report).
4756
+ */
4757
+ fillComputedFields(table: string, llm: FillLlm, opts?: {
4758
+ batchSize?: number;
4759
+ maxRows?: number;
4760
+ }): Promise<FieldFillReport>;
4761
+ /** Every table that has AI computed fields (for open-time backfill). */
4762
+ aiComputedFieldTables(): string[];
3632
4763
  /** Recompute parent rollup(s) for the FK values carried on a source row. */
3633
4764
  private _propagateRollupsFromRow;
3634
4765
  /** Recompute the parent rollup(s) fed by a changed source row (fetched by id). */
@@ -3653,6 +4784,21 @@ declare class Lattice {
3653
4784
  * @since 0.17.0
3654
4785
  */
3655
4786
  updateReturning(table: string, id: PkLookup, row: Partial<Row>): Promise<Row>;
4787
+ /**
4788
+ * Permanently delete a row via `DELETE FROM`. This is a **hard delete** —
4789
+ * the row is removed from the table, not soft-deleted via a `deleted_at` column.
4790
+ *
4791
+ * On tables with `changelog: true`, the full previous row is captured and
4792
+ * appended to the changelog before removal, making the delete auditable and
4793
+ * recoverable via {@link rollback}. On tables without a changelog, the row
4794
+ * is gone permanently.
4795
+ *
4796
+ * Side effects:
4797
+ * - If the table has materialized rollups fed by child rows, a deleted child
4798
+ * is removed from its parent's rollup aggregates.
4799
+ * - Write hooks and audit events are fired.
4800
+ * - Embeddings are synced (if the table has embedding definitions).
4801
+ */
3656
4802
  delete(table: string, id: PkLookup, provenance?: ChangeProvenance): Promise<void>;
3657
4803
  /**
3658
4804
  * Record a DERIVED observation about a row WITHOUT mutating the canonical row.
@@ -3750,6 +4896,21 @@ declare class Lattice {
3750
4896
  * decrypt deps are wired through `_encryption`, so the query/get decryption
3751
4897
  * asymmetry is preserved: only query/get invoke them. */
3752
4898
  private get _queryCore();
4899
+ /** Optional host-supplied replacement for the pre-render reverse-sync drain. */
4900
+ private _autoRenderDrainOverride;
4901
+ /**
4902
+ * Route non-error render notices (e.g. an edited generated rollup being
4903
+ * restored) somewhere visible. Default: console.warn. The GUI wires its
4904
+ * activity feed here.
4905
+ */
4906
+ setRenderNoticeHandler(handler: ((message: string) => void) | null): void;
4907
+ /**
4908
+ * Replace the pre-render manual-edit drain (see the auto-render scheduler's
4909
+ * drain dep). The GUI wires its file-loopback watcher here so drained edits go
4910
+ * through the full mutation path (changelog + activity feed + undo) instead of
4911
+ * the core changelog-only apply.
4912
+ */
4913
+ setAutoRenderDrain(drain: ((outputDir: string) => Promise<void>) | null): void;
3753
4914
  /** Lazily-constructed auto-render scheduler (see src/render/auto-render.ts). */
3754
4915
  private get _autoRender();
3755
4916
  /**
@@ -3828,6 +4989,12 @@ declare class Lattice {
3828
4989
  */
3829
4990
  private _expandRelations;
3830
4991
  count(table: string, opts?: CountOptions): Promise<number>;
4992
+ /**
4993
+ * Bounded variant of {@link count}: stops after `opts.cap + 1` matching rows
4994
+ * (default cap 1000) so it stays cheap on large tables. Returns the exact count
4995
+ * when `<= cap`, else `cap + 1` to signal "more than cap" (render as "cap+").
4996
+ */
4997
+ boundedCount(table: string, opts?: BoundedCountOptions): Promise<number>;
3831
4998
  /**
3832
4999
  * SQL-side aggregation — `COUNT`/`SUM`/`AVG`/`MIN`/`MAX` with optional
3833
5000
  * `GROUP BY` and `HAVING`, computed in the database so only the grouped result
@@ -4013,6 +5180,14 @@ declare class Lattice {
4013
5180
  * `reverseSync`. Compares file hashes against the current manifest, so a
4014
5181
  * render-written file is recognized as an echo and skipped.
4015
5182
  */
5183
+ /**
5184
+ * The post-render reconciliation pass: prune files the prior manifest managed
5185
+ * that the new render no longer produces (deleted rows, renamed roots, retired
5186
+ * rollups) — hash-guarded so a manually edited file is never deleted. Runs
5187
+ * automatically after auto/background renders and at workspace open; exposed
5188
+ * for callers that drive `render()` themselves.
5189
+ */
5190
+ reconcileRenderedTree(outputDir: string, prevManifest: LatticeManifest | null, newManifest: LatticeManifest | null): Promise<CleanupResult>;
4016
5191
  reverseSyncFromFiles(outputDir: string, opts?: ReverseSyncProcessOptions): Promise<ReverseSyncResult>;
4017
5192
  watch(outputDir: string, opts?: WatchOptions): Promise<StopFn>;
4018
5193
  on(event: 'audit', handler: EventHandler<AuditEvent>): this;
@@ -4087,6 +5262,17 @@ declare class Lattice {
4087
5262
  private _serializePkLookup;
4088
5263
  /** Returns a rejected Promise if not initialized; null if ready. */
4089
5264
  private _fireWriteHooks;
5265
+ /**
5266
+ * Suspend auto-render (re-entrant) for the duration of a bulk operation — e.g.
5267
+ * ingesting a folder of hundreds of files. Writes still record their render
5268
+ * scope, but no render fires until {@link resumeAutoRender} balances every
5269
+ * pause, at which point ONE coalesced render covers everything. This removes
5270
+ * the O(N²) "render-per-file" blowup where each of N writes re-scanned the
5271
+ * whole (growing) file set. Always pair with resumeAutoRender in a `finally`.
5272
+ */
5273
+ pauseAutoRender(): this;
5274
+ /** Balance a {@link pauseAutoRender}; the last resume arms one coalesced render. */
5275
+ resumeAutoRender(): this;
4090
5276
  /**
4091
5277
  * Turn on automatic rendering into `outputDir`. After this, every insert /
4092
5278
  * update / delete debounce-triggers a re-render (coalesced, so a bulk seed
@@ -4152,242 +5338,44 @@ declare class Lattice {
4152
5338
  recentChanges(opts?: {
4153
5339
  table?: string;
4154
5340
  since?: string;
4155
- limit?: number;
4156
- }): Promise<ChangeEntry[]>;
4157
- /**
4158
- * Rollback a specific change by applying the inverse operation.
4159
- * The rollback itself is recorded as a new changelog entry.
4160
- */
4161
- rollback(changeId: string): Promise<void>;
4162
- /**
4163
- * Show field-level diff between two changelog entries for the same row.
4164
- */
4165
- diff(table: string, id: string, fromChangeId: string, toChangeId: string): Promise<Record<string, {
4166
- old: unknown;
4167
- new: unknown;
4168
- }>>;
4169
- /**
4170
- * Reconstruct the row state at a specific changelog entry by replaying
4171
- * all operations up to and including that entry.
4172
- */
4173
- snapshot(table: string, id: string, changeId: string): Promise<Record<string, unknown>>;
4174
- /**
4175
- * Manually prune changelog entries based on the configured retention policy.
4176
- * Also callable directly for on-demand cleanup.
4177
- */
4178
- pruneChangelog(): Promise<void>;
4179
- private _notInitError;
4180
- /**
4181
- * Returns a rejected Promise if any of the given column names are not present
4182
- * in the table's schema; null if all columns are valid.
4183
- *
4184
- * Applied on the read path (query/count) to validate WHERE and filter column
4185
- * names before they are interpolated into SQL. The write path strips unknown
4186
- * columns via _filterToSchemaColumns; the read path rejects instead to avoid
4187
- * silently discarding intended filter conditions.
4188
- *
4189
- * Unregistered tables (accessed via the raw `.db` handle) are passed through.
4190
- */
4191
- private _invalidColumnError;
4192
- private _assertNotInit;
4193
- }
4194
-
4195
- /**
4196
- * Scalar types recognised in `lattice.config.yml` field definitions.
4197
- *
4198
- * | YAML type | SQLite type | TypeScript type |
4199
- * | ----------- | ----------- | --------------- |
4200
- * | `uuid` | TEXT | string |
4201
- * | `text` | TEXT | string |
4202
- * | `integer` | INTEGER | number |
4203
- * | `int` | INTEGER | number |
4204
- * | `real` | REAL | number |
4205
- * | `float` | REAL | number |
4206
- * | `boolean` | INTEGER | boolean |
4207
- * | `bool` | INTEGER | boolean |
4208
- * | `datetime` | TEXT | string |
4209
- * | `date` | TEXT | string |
4210
- * | `blob` | BLOB | Buffer |
4211
- */
4212
- type LatticeFieldType = 'uuid' | 'text' | 'integer' | 'int' | 'real' | 'float' | 'boolean' | 'bool' | 'datetime' | 'date' | 'blob';
4213
- /**
4214
- * A single field (column) definition in a `lattice.config.yml` entity.
4215
- *
4216
- * @example
4217
- * ```yaml
4218
- * id: { type: uuid, primaryKey: true }
4219
- * title: { type: text, required: true }
4220
- * status: { type: text, default: open }
4221
- * assignee_id: { type: uuid }
4222
- * score: { type: integer, default: 0 }
4223
- * ```
4224
- */
4225
- interface LatticeFieldDef {
4226
- /** Column data type */
4227
- type: LatticeFieldType;
4228
- /** Mark this column as the table's primary key */
4229
- primaryKey?: boolean;
4230
- /** Column is NOT NULL */
4231
- required?: boolean;
4232
- /** SQL DEFAULT value */
4233
- default?: string | number | boolean;
4234
- /**
4235
- * Per-column audience (Stage-0 scaffolding for the per-viewer enrichment
4236
- * model). Names who may see this column's value in a cloud. Omitted ⇒
4237
- * `row-audience` — the value is visible to exactly whoever can see the row,
4238
- * which is today's behavior, so leaving it unset changes nothing. Later
4239
- * stages parse a richer grammar (e.g. `subject+role:hr`) and generate a
4240
- * cell-masking view from it; Stage-0 only records the metadata.
4241
- */
4242
- audience?: string;
4243
- /**
4244
- * DEPRECATED (3.x) per-field foreign-key shorthand: `ref: <targetTable>` declared
4245
- * a `belongsTo` whose relation name is the field name with a trailing `_id`
4246
- * stripped. Superseded by the explicit entity-level `relations:` block. 4.0 still
4247
- * PARSES it (converted to a `belongsTo` in-memory) so existing 3.0+ configs keep
4248
- * working, and the GUI silently rewrites it to `relations:` on open so configs
4249
- * migrate forward. A future major may drop this once configs have upgraded.
4250
- */
4251
- ref?: string;
4252
- }
4253
- /**
4254
- * Inline render spec inside YAML — a flat object alternative to `TemplateRenderSpec`.
4255
- *
4256
- * @example
4257
- * ```yaml
4258
- * render:
4259
- * template: default-list
4260
- * formatRow: "{{title}} — {{status}}"
4261
- * ```
4262
- */
4263
- interface LatticeEntityRenderSpec {
4264
- template: string;
4265
- formatRow?: string;
4266
- }
4267
- /**
4268
- * A single entity (table) definition in `lattice.config.yml`.
4269
- *
4270
- * @example
4271
- * ```yaml
4272
- * ticket:
4273
- * fields:
4274
- * id: { type: uuid, primaryKey: true }
4275
- * title: { type: text, required: true }
4276
- * assignee_id: { type: uuid }
4277
- * relations:
4278
- * assignee:
4279
- * type: belongsTo
4280
- * table: user
4281
- * foreignKey: assignee_id
4282
- * render: default-list
4283
- * outputFile: context/TICKETS.md
4284
- * ```
4285
- */
4286
- interface LatticeEntityDef {
4287
- /** Column definitions */
4288
- fields: Record<string, LatticeFieldDef>;
4289
- /**
4290
- * Explicit `belongsTo` relations for this entity, keyed by relation name.
4291
- *
4292
- * Each entry declares a foreign key on THIS entity pointing at another
4293
- * table: `{ type: belongsTo, table, foreignKey, references? }`. The
4294
- * `foreignKey` names a plain field on this entity; `references` is the
4295
- * column on the related table (defaults to its primary key). The relation
4296
- * name (the map key) is whatever you choose — it is not derived from the
4297
- * field name.
4298
- *
4299
- * @example
4300
- * ```yaml
4301
- * relations:
4302
- * assignee:
4303
- * type: belongsTo
4304
- * table: user
4305
- * foreignKey: assignee_id
4306
- * # references: id # optional; defaults to the target's primary key
4307
- * ```
4308
- */
4309
- relations?: Record<string, BelongsToRelation>;
4310
- /**
4311
- * How to render rows into context text.
4312
- * Accepts the same forms as `TableDefinition.render`:
4313
- * - A `BuiltinTemplateName` string (e.g. `default-list`)
4314
- * - A `{ template, formatRow }` object for hooks
4315
- */
4316
- render?: string | LatticeEntityRenderSpec;
4317
- /** Render output file path (relative to the config file directory) */
4318
- outputFile: string;
4319
- /**
4320
- * Optional explicit primary key override.
4321
- * If omitted, the field with `primaryKey: true` is used.
4322
- * Accepts a single column name or an array for composite keys.
4323
- */
4324
- primaryKey?: string | string[];
4325
- }
4326
- /**
4327
- * The top-level `lattice.config.yml` document.
4328
- *
4329
- * @example
4330
- * ```yaml
4331
- * db: ./data/app.db
4332
- * entities:
4333
- * ticket:
4334
- * fields:
4335
- * id: { type: uuid, primaryKey: true }
4336
- * title: { type: text, required: true }
4337
- * render: default-list
4338
- * outputFile: context/TICKETS.md
4339
- * ```
4340
- */
4341
- interface LatticeConfig {
4342
- /** Path to the SQLite database file (relative to the config file) */
4343
- db: string;
4344
- /** Entity (table) definitions */
4345
- entities: Record<string, LatticeEntityDef>;
4346
- /** Entity context directory definitions */
4347
- entityContexts?: Record<string, LatticeEntityContextDef>;
4348
- }
4349
- /**
4350
- * Source spec in YAML config — either the shorthand string 'self' or an object.
4351
- */
4352
- type LatticeEntityContextSourceDef = 'self' | {
4353
- type: 'hasMany';
4354
- table: string;
4355
- foreignKey: string;
4356
- references?: string;
4357
- } | {
4358
- type: 'manyToMany';
4359
- junctionTable: string;
4360
- localKey: string;
4361
- remoteKey: string;
4362
- remoteTable: string;
4363
- references?: string;
4364
- } | {
4365
- type: 'belongsTo';
4366
- table: string;
4367
- foreignKey: string;
4368
- references?: string;
4369
- };
4370
- /** A single per-entity file spec in YAML config */
4371
- interface LatticeEntityContextFileDef {
4372
- source: LatticeEntityContextSourceDef;
4373
- template: string;
4374
- budget?: number;
4375
- omitIfEmpty?: boolean;
4376
- }
4377
- /** Entity context definition in YAML config */
4378
- interface LatticeEntityContextDef {
4379
- slug: string;
4380
- directoryRoot?: string;
4381
- protectedFiles?: string[];
4382
- index?: {
4383
- outputFile: string;
4384
- render: string;
4385
- };
4386
- files: Record<string, LatticeEntityContextFileDef>;
4387
- combined?: {
4388
- outputFile: string;
4389
- exclude?: string[];
4390
- };
5341
+ limit?: number;
5342
+ }): Promise<ChangeEntry[]>;
5343
+ /**
5344
+ * Rollback a specific change by applying the inverse operation.
5345
+ * The rollback itself is recorded as a new changelog entry.
5346
+ */
5347
+ rollback(changeId: string): Promise<void>;
5348
+ /**
5349
+ * Show field-level diff between two changelog entries for the same row.
5350
+ */
5351
+ diff(table: string, id: string, fromChangeId: string, toChangeId: string): Promise<Record<string, {
5352
+ old: unknown;
5353
+ new: unknown;
5354
+ }>>;
5355
+ /**
5356
+ * Reconstruct the row state at a specific changelog entry by replaying
5357
+ * all operations up to and including that entry.
5358
+ */
5359
+ snapshot(table: string, id: string, changeId: string): Promise<Record<string, unknown>>;
5360
+ /**
5361
+ * Manually prune changelog entries based on the configured retention policy.
5362
+ * Also callable directly for on-demand cleanup.
5363
+ */
5364
+ pruneChangelog(): Promise<void>;
5365
+ private _notInitError;
5366
+ /**
5367
+ * Returns a rejected Promise if any of the given column names are not present
5368
+ * in the table's schema; null if all columns are valid.
5369
+ *
5370
+ * Applied on the read path (query/count) to validate WHERE and filter column
5371
+ * names before they are interpolated into SQL. The write path strips unknown
5372
+ * columns via _filterToSchemaColumns; the read path rejects instead to avoid
5373
+ * silently discarding intended filter conditions.
5374
+ *
5375
+ * Unregistered tables (accessed via the raw `.db` handle) are passed through.
5376
+ */
5377
+ private _invalidColumnError;
5378
+ private _assertNotInit;
4391
5379
  }
4392
5380
 
4393
5381
  /** Output of a successful config parse — ready to hand to Lattice. */
@@ -4410,6 +5398,11 @@ interface ParsedConfig {
4410
5398
  table: string;
4411
5399
  definition: EntityContextDefinition;
4412
5400
  }[];
5401
+ /** Computed-table (read-only SQL projection) definitions in declaration order */
5402
+ computedTables: readonly {
5403
+ name: string;
5404
+ definition: ComputedTableDef;
5405
+ }[];
4413
5406
  }
4414
5407
  /**
4415
5408
  * Read, parse, and validate a `lattice.config.yml` file.
@@ -4498,9 +5491,9 @@ declare function fixSchemaConflicts(db: Database.Database, checks: {
4498
5491
  }[]): void;
4499
5492
 
4500
5493
  /**
4501
- * Derive a 256-bit AES key from a master password using scrypt.
4502
- * The salt is fixed per Lattice instance — callers should use a unique
4503
- * master key per database.
5494
+ * Derive a 256-bit AES key from a master password using scrypt (memoized by the
5495
+ * master-key value). The salt is fixed per Lattice instance — callers should use a
5496
+ * unique master key per database.
4504
5497
  */
4505
5498
  declare function deriveKey(masterKey: string): Buffer;
4506
5499
  /**
@@ -4569,7 +5562,7 @@ declare function markdownTable(rows: Row[], columns: MarkdownTableColumn[]): str
4569
5562
  * @example `slugify('My Agent Name') // 'my-agent-name'`
4570
5563
  * @example `slugify('José García') // 'jose-garcia'`
4571
5564
  */
4572
- declare function slugify(name: string): string;
5565
+ declare function slugify$1(name: string): string;
4573
5566
  /**
4574
5567
  * Truncate content at a character budget.
4575
5568
  *
@@ -4892,11 +5885,39 @@ declare class DenoSqliteAdapter implements StorageAdapter {
4892
5885
  private readonly _path;
4893
5886
  private readonly _wal;
4894
5887
  private readonly _busyTimeout;
5888
+ /**
5889
+ * Prepared-statement cache, keyed by SQL text. This is load-bearing, not an
5890
+ * optimization: `node:sqlite`'s `DatabaseSync.prepare()` — unlike `better-sqlite3`,
5891
+ * which caches compiled statements internally — compiles a FRESH native
5892
+ * `sqlite3_stmt` on every call and never finalizes it eagerly. Preparing per call
5893
+ * (as the raw adapter surface does) therefore leaks a native statement every
5894
+ * `run`/`get`/`all`; under a bulk-write loop (e.g. ingesting a folder of files, or
5895
+ * the change-probe watch loop) these accumulate far faster than GC reclaims them and
5896
+ * abort the runtime with a native memory blowup. Caching by SQL text keeps the live
5897
+ * native-statement count flat — the same behavior `better-sqlite3` gives for free.
5898
+ */
5899
+ private readonly _stmtCache;
5900
+ private _stmtCacheMisses;
4895
5901
  constructor(path: string, options?: {
4896
5902
  wal?: boolean;
4897
5903
  busyTimeout?: number;
4898
5904
  });
4899
5905
  get db(): NodeDatabaseSync;
5906
+ /**
5907
+ * Return the compiled statement for `sql`, reusing a cached one when present. A
5908
+ * `node:sqlite` statement is reusable across executions (each `run`/`get`/`all`
5909
+ * re-binds its params), and SQLite's `prepare_v2` auto-recompiles a cached statement
5910
+ * across a schema change (ALTER TABLE) transparently — so reuse is safe. Bounded so a
5911
+ * caller that inlines values into SQL (unbounded distinct text) can't grow it without
5912
+ * limit; on overflow the whole cache is dropped (evicted statements finalize on GC)
5913
+ * and rebuilt from the next calls.
5914
+ */
5915
+ private _prepared;
5916
+ /** Diagnostics: live cached-statement count + total compiles (cache misses). */
5917
+ stmtCacheStats(): {
5918
+ size: number;
5919
+ misses: number;
5920
+ };
4900
5921
  open(): void;
4901
5922
  close(): void;
4902
5923
  run(sql: string, params?: unknown[]): void;
@@ -5207,8 +6228,23 @@ interface Connector {
5207
6228
  * MUST page rather than load everything at once (bounded reads).
5208
6229
  */
5209
6230
  listChanges(toolkit: string, model: string, ctx: ListChangesContext): AsyncIterable<ExternalRecord>;
5210
- /** Revoke a connected account (teardown). */
6231
+ /**
6232
+ * Optional batch lifecycle. The sync engine calls `beginSyncSession` before a
6233
+ * connector's models are synced and `endSyncSession` after (even on error), so a
6234
+ * connector can open ONE shared resource (e.g. a single MCP transport) and reuse
6235
+ * it across every `listChanges` call instead of reconnecting per parent key.
6236
+ * Connectors that omit these fall back to per-call open (behavior unchanged).
6237
+ */
6238
+ beginSyncSession?(connectionId: string): Promise<void>;
6239
+ endSyncSession?(connectionId: string): Promise<void>;
6240
+ /** Revoke a connected account (teardown). May retain non-secret reconnect state. */
5211
6241
  disconnect(connectionId: string): Promise<void>;
6242
+ /**
6243
+ * Remove ALL local state for a connection, including anything `disconnect`
6244
+ * retained for reconnect (e.g. a stored server URL). Called on hard teardown,
6245
+ * after the registry row is deleted. Optional; omit when disconnect is total.
6246
+ */
6247
+ purgeConnection?(connectionId: string): Promise<void>;
5212
6248
  }
5213
6249
  /**
5214
6250
  * A connector that connects via direct credentials the member pastes in — an API
@@ -5239,14 +6275,109 @@ interface CredentialConnector extends Connector {
5239
6275
  * layer uses this to decide whether to serve the credential connect form.
5240
6276
  */
5241
6277
  declare function isCredentialConnector(c: Connector): c is CredentialConnector;
6278
+ /**
6279
+ * How Lattice reaches one MCP server's tools. Everything runs on the local
6280
+ * machine — Lattice IS the MCP client. A remote server is reached over Streamable
6281
+ * HTTP (authorized with that server's own OAuth); a local server runs as a stdio
6282
+ * child process (fully offline, no OAuth). No data is routed through any cloud
6283
+ * middleman (no Lattice-cloud, no model inference).
6284
+ */
6285
+ interface McpServerSpec {
6286
+ /** Stable server name (e.g. `'atlassian'`, `'monday'`). */
6287
+ name: string;
6288
+ /** Remote Streamable-HTTP endpoint. Omit for a stdio server. */
6289
+ url?: string;
6290
+ /** Local stdio command (the server runs as a child process, fully offline). */
6291
+ command?: string;
6292
+ /** Args for the stdio command. */
6293
+ args?: string[];
6294
+ /**
6295
+ * Transport. Inferred when omitted: `command` → `'stdio'`, a `url` ending in
6296
+ * `/sse` → `'sse'` (the legacy Server-Sent-Events transport many hosted MCP
6297
+ * servers still use), otherwise `'http'` (Streamable HTTP).
6298
+ */
6299
+ transport?: 'http' | 'sse' | 'stdio';
6300
+ /** Whether the server requires OAuth. Defaults to true for HTTP/SSE, false for stdio. */
6301
+ oauth?: boolean;
6302
+ }
6303
+ /** Begin-connect result: either an OAuth redirect to complete, or an immediate connection. */
6304
+ type McpBeginResult = {
6305
+ kind: 'redirect';
6306
+ redirectUrl: string;
6307
+ pendingId: string;
6308
+ } | {
6309
+ kind: 'connected';
6310
+ connectionId: string;
6311
+ displayName: string | null;
6312
+ };
6313
+ /**
6314
+ * A connector whose data comes from an MCP server it reads over a local transport
6315
+ * (mechanism 2 — Lattice is the MCP client, per-server OAuth, tokens in the
6316
+ * machine-local encrypted store). Connecting means authorizing that server, not a
6317
+ * broker or a Claude subscription. The sync engine is unchanged — `listChanges`
6318
+ * calls MCP read tools instead of a bespoke REST client.
6319
+ */
6320
+ interface McpConnector extends Connector {
6321
+ /** The MCP server(s) this toolkit reads from. */
6322
+ mcpServers(toolkit: string): McpServerSpec[];
6323
+ /**
6324
+ * Begin connecting. For an OAuth server, run discovery + stash PKCE state and
6325
+ * return a redirect the GUI opens (system browser on desktop). For a local/open
6326
+ * server, validate via `tools/list` and return the connection immediately.
6327
+ * `redirectUri` is where the GUI receives the code; `serverUrl` overrides the
6328
+ * toolkit default (used by the generic bring-your-own-URL connector).
6329
+ * `clientInfo` is a pre-registered OAuth client (id + optional secret) for
6330
+ * authorization servers that support neither a client-ID metadata document nor
6331
+ * dynamic registration. `targetConnectorId` marks a reconnect of an existing
6332
+ * registry row rather than a new connection.
6333
+ */
6334
+ beginConnect(userId: string, toolkit: string, opts?: {
6335
+ redirectUri?: string;
6336
+ serverUrl?: string;
6337
+ clientInfo?: {
6338
+ client_id: string;
6339
+ client_secret?: string;
6340
+ };
6341
+ targetConnectorId?: string;
6342
+ }): Promise<McpBeginResult>;
6343
+ /**
6344
+ * Finish an OAuth connect: exchange the `code`, store the token under
6345
+ * `mcp_creds:<connectionId>`, and return the connection. `serverName` is the
6346
+ * server's self-reported name from the MCP handshake (display-name material);
6347
+ * `targetConnectorId` echoes the reconnect target recorded at begin. Throws
6348
+ * loudly on a mismatched state or a failed exchange — never a silent default.
6349
+ */
6350
+ completeConnect(pendingId: string, params: {
6351
+ code: string;
6352
+ state?: string;
6353
+ }): Promise<{
6354
+ connectionId: string;
6355
+ displayName: string | null;
6356
+ serverName?: string;
6357
+ targetConnectorId?: string;
6358
+ }>;
6359
+ /**
6360
+ * OPTIONAL: discover the server's record shapes and persist a typed schema descriptor for
6361
+ * this connection, so {@link Connector.models} emits one typed table per record kind (namespaced
6362
+ * by `prefix`, the server brand). Present on the introspective generic connector; absent on
6363
+ * fixed-schema connectors. Best-effort — resolves null when the server exposes nothing modelable.
6364
+ */
6365
+ introspect?(connectionId: string, toolkit: string, prefix: string): Promise<unknown>;
6366
+ }
6367
+ /** True when `c` is an {@link McpConnector} (connects via an MCP server, not credentials). */
6368
+ declare function isMcpConnector(c: Connector): c is McpConnector;
5242
6369
 
5243
6370
  /**
5244
6371
  * The connector catalog — the single place every built-in connector is wired.
5245
6372
  *
5246
6373
  * Routes, the sidebar, the settings panel, and sync-all all iterate this list via
5247
- * the data-driven API ({@link Connector.presentation} / `credentialFields` /
5248
- * `models`), so adding a connector is a new module under `src/connectors/<toolkit>/`
5249
- * plus one line here no route, GUI, or SPI changes.
6374
+ * the data-driven API ({@link Connector.presentation} / `mcpServers` / `models`).
6375
+ *
6376
+ * There is exactly ONE built-in connector: the generic MCP connector. Lattice is
6377
+ * a local MCP client — the user supplies any MCP server URL, authorizes it with
6378
+ * that server's own OAuth, and each added server is its own connection. Nothing
6379
+ * is routed through a cloud middleman, and no provider-specific connector code
6380
+ * exists; a provider is just another MCP server URL.
5250
6381
  *
5251
6382
  * Named `catalog.ts` (not `registry.ts`) to avoid confusion with the
5252
6383
  * `__lattice_connectors` DB table managed in `registry.ts`: this is the in-memory
@@ -5254,8 +6385,8 @@ declare function isCredentialConnector(c: Connector): c is CredentialConnector;
5254
6385
  */
5255
6386
 
5256
6387
  /**
5257
- * Every built-in connector, fresh instances per call. The GUI server passes the
5258
- * result to the connectors routes; toolkit ids must be unique across the list.
6388
+ * The built-in connectors, fresh instances per call. The GUI server passes the
6389
+ * result to the connectors routes.
5259
6390
  */
5260
6391
  declare function builtinConnectors(): Connector[];
5261
6392
 
@@ -5327,7 +6458,7 @@ declare function getConnectorByToolkit(db: Lattice, toolkit: string, connectedBy
5327
6458
  */
5328
6459
  declare function listConnectors(db: Lattice, connectedBy?: string): Promise<ConnectorRecord[]>;
5329
6460
  /** Update a connector's backend connection id + mark it connected (re-auth/reuse). */
5330
- declare function updateConnectorConnection(db: Lattice, id: string, connectionRef: string): Promise<void>;
6461
+ declare function updateConnectorConnection(db: Lattice, id: string, connectionRef: string, toolkit?: string): Promise<void>;
5331
6462
  /** Record a sync outcome: success stamps `last_sync_at` + clears the error. */
5332
6463
  declare function recordSync(db: Lattice, id: string, outcome: {
5333
6464
  ok: true;
@@ -5349,296 +6480,577 @@ declare function setConnectorStatus(db: Lattice, id: string, status: ConnectorSt
5349
6480
  declare function deleteConnectorRecord(db: Lattice, id: string): Promise<void>;
5350
6481
 
5351
6482
  /**
5352
- * The Jira connector — a {@link Connector} that talks to Jira Cloud's REST + Agile
5353
- * APIs DIRECTLY via the optional `jira.js` dependency, authenticated with the
5354
- * user's OWN Atlassian credentials (site URL + email + API token, HTTP Basic).
5355
- * No broker, no extra API key.
6483
+ * Shared connector errors.
5356
6484
  *
5357
- * The SPI is OAuth-shaped (authorize→redirect→completeAuth); Jira uses direct
5358
- * credentials instead, so `authorize`/`completeAuth` are not part of its flow
5359
- * the GUI collects credentials and calls {@link JiraConnector.connect}, which
5360
- * validates them against Jira and stores them encrypted. `listChanges` (sync) and
5361
- * `disconnect` (teardown) satisfy the SPI normally, keyed by the stored
5362
- * connection. The schema (the six connected models) lives in `./models.ts`; this
5363
- * file is the fetch/auth half.
6485
+ * Lives in its own module (not inside any one connector) so every connector and
6486
+ * the GUI routes reference a single {@link ConnectorUnavailableError} identity
6487
+ * the route layer maps it to a 422 via `instanceof`, so a split identity would
6488
+ * silently break that mapping.
5364
6489
  */
5365
-
5366
- /** The user's Atlassian credentials for one Jira connection. */
5367
- interface JiraCreds {
5368
- /** Site base URL, e.g. `https://your-domain.atlassian.net`. */
5369
- site: string;
5370
- /** Atlassian account email (the Basic-auth username). */
5371
- email: string;
5372
- /** Atlassian API token (the Basic-auth password) — a user secret. */
5373
- apiToken: string;
5374
- }
5375
- /** Thrown when the connector is used but its prerequisites are missing. */
6490
+ /** Thrown when a connector is used but its prerequisites are missing. */
5376
6491
  declare class ConnectorUnavailableError extends Error {
5377
6492
  constructor(message: string);
5378
6493
  }
5379
- /** Read the stored credentials for a connection, or null. */
5380
- declare function getJiraCreds(connectionId: string): JiraCreds | null;
5381
- /** Persist credentials for a connection to the machine-local encrypted store. */
5382
- declare function setJiraCreds(connectionId: string, creds: JiraCreds): void;
5383
- /** Remove the stored credentials for a connection. */
5384
- declare function clearJiraCreds(connectionId: string): void;
5385
- type Json$1 = Record<string, unknown>;
5386
- /** The minimal Jira surface the connector depends on (all calls return raw REST shapes). */
5387
- interface JiraClient {
5388
- /** Validate the credentials + return the authenticated account (`GET /myself`). */
5389
- myself(): Promise<Json$1>;
5390
- searchIssues(args: {
5391
- jql: string;
5392
- nextPageToken?: string;
5393
- maxResults: number;
5394
- fields?: string[];
5395
- }): Promise<{
5396
- issues: Json$1[];
5397
- nextPageToken?: string;
5398
- }>;
5399
- searchProjects(args: {
5400
- startAt: number;
5401
- maxResults: number;
5402
- }): Promise<{
5403
- values: Json$1[];
5404
- total?: number;
5405
- isLast?: boolean;
5406
- }>;
5407
- getComments(args: {
5408
- issueIdOrKey: string;
5409
- startAt: number;
5410
- maxResults: number;
5411
- }): Promise<{
5412
- comments: Json$1[];
5413
- total?: number;
5414
- }>;
5415
- getAllUsers(args: {
5416
- startAt: number;
5417
- maxResults: number;
5418
- }): Promise<Json$1[]>;
5419
- getAllBoards(args: {
5420
- startAt: number;
5421
- maxResults: number;
5422
- }): Promise<{
5423
- values: Json$1[];
5424
- total?: number;
5425
- isLast?: boolean;
5426
- }>;
5427
- getAllSprints(args: {
5428
- boardId: number;
5429
- startAt: number;
5430
- maxResults: number;
5431
- }): Promise<{
5432
- values: Json$1[];
5433
- total?: number;
5434
- isLast?: boolean;
5435
- }>;
6494
+
6495
+ /**
6496
+ * The MCP transport seam.
6497
+ *
6498
+ * An {@link McpConnector} calls a read tool by name and gets back the tool's raw
6499
+ * JSON result; *how* the call reaches the server is the transport's concern. This
6500
+ * keeps the connector modules (schema + mappers) and the sync engine completely
6501
+ * decoupled from the MCP SDK and lets tests inject a fake transport with canned
6502
+ * tool JSON instead of standing up a real server.
6503
+ *
6504
+ * There is exactly one production implementation ({@link './direct-transport.ts'}):
6505
+ * Lattice as a local MCP client over Streamable HTTP (remote servers) or stdio
6506
+ * (local server processes). Nothing routes through a cloud middleman.
6507
+ */
6508
+ /** One MCP read-tool invocation. */
6509
+ interface McpToolCall {
6510
+ /** Tool name (e.g. `'search_threads'`). */
6511
+ tool: string;
6512
+ /** Tool arguments (the tool's input schema). */
6513
+ args: Record<string, unknown>;
6514
+ }
6515
+ /** A tool discovered on the server (name + optional description), for connect-time validation. */
6516
+ interface McpToolInfo {
6517
+ name: string;
6518
+ description?: string;
6519
+ }
6520
+ /** A resource advertised by the server (`resources/list`) — its "available files". */
6521
+ interface McpResourceInfo {
6522
+ name: string;
6523
+ uri: string;
6524
+ description?: string;
6525
+ mimeType?: string;
5436
6526
  }
5437
6527
  /**
5438
- * Build a {@link JiraClient} from `jira.js` (lazy-imported) + the user's creds.
5439
- * Throws {@link ConnectorUnavailableError} if the optional dependency is absent.
5440
- * The dynamic import uses non-literal specifiers so TypeScript does not statically
5441
- * resolve `jira.js` — it need not be installed to compile latticesql.
6528
+ * A live connection to one MCP server. Implementations MUST NOT hold the
6529
+ * connection open across calls unless {@link close} is honored connectors open
6530
+ * a transport, drain a model's pages, and close.
5442
6531
  */
5443
- declare function loadJiraClient(creds: JiraCreds): Promise<JiraClient>;
5444
- declare class JiraConnector implements CredentialConnector {
5445
- private readonly clientFactory;
5446
- private readonly credsLoader;
5447
- readonly connector = "jira";
5448
- /**
5449
- * @param clientFactory builds a {@link JiraClient} from creds (default: the lazy
5450
- * `jira.js` loader; tests inject a fake).
5451
- * @param credsLoader resolves a connection id to its stored creds (default: the
5452
- * machine-local store; tests inject a fake).
5453
- */
5454
- constructor(clientFactory?: (creds: JiraCreds) => Promise<JiraClient>, credsLoader?: (connectionId: string) => JiraCreds | null);
5455
- toolkits(): string[];
5456
- models(toolkit: string): ConnectedModelDef[];
5457
- presentation(toolkit: string): ToolkitPresentation;
5458
- credentialFields(): CredentialField[];
5459
- helpUrl(): string;
6532
+ interface McpTransport {
6533
+ /** List the callable tools on the server (connect-time validation + discovery). */
6534
+ listTools(): Promise<McpToolInfo[]>;
6535
+ /** Call one read tool; returns the parsed JSON content the tool produced. */
6536
+ callTool(call: McpToolCall): Promise<unknown>;
5460
6537
  /**
5461
- * Jira authenticates with direct credentials, not an OAuth redirect. The GUI
5462
- * collects the site/email/token and calls {@link connect}; these SPI methods are
5463
- * therefore not part of the Jira flow and throw a clear, actionable error.
6538
+ * List the server's advertised resources (standard MCP `resources/list`).
6539
+ * Servers without the resources capability yield `[]`, never an error.
5464
6540
  */
5465
- authorize(_userId: string, _toolkit: string): Promise<AuthorizeResult>;
5466
- completeAuth(_userId: string, _toolkit: string): Promise<ConnectionResult>;
5467
- /**
5468
- * Validate Atlassian credentials against Jira (`GET /myself`) and, on success,
5469
- * store them encrypted under a fresh connection id. Reads the submitted
5470
- * `site` / `email` / `token` values (the wire key stays `token`; it maps to the
5471
- * internal `apiToken`). Returns the connection id (recorded in the registry by
5472
- * the caller) and the validated account display name (for the UI). Throws a
5473
- * clear, actionable error if the inputs are missing, the site isn't a URL, or
5474
- * the credentials are invalid.
5475
- */
5476
- connect(creds: Record<string, string>): Promise<{
6541
+ listResources(): Promise<McpResourceInfo[]>;
6542
+ /** The server's self-reported identity from the `initialize` handshake, if known. */
6543
+ serverInfo?(): {
6544
+ name?: string;
6545
+ title?: string;
6546
+ version?: string;
6547
+ } | undefined;
6548
+ /** Close the underlying client/session. */
6549
+ close(): Promise<void>;
6550
+ }
6551
+ /**
6552
+ * Builds a transport for one connected MCP server. The connector base takes this
6553
+ * as a constructor seam so tests inject a fake and production injects the direct
6554
+ * client. `connectionId` keys the stored OAuth token for the server.
6555
+ */
6556
+ type McpTransportFactory = (server: McpServerRef) => Promise<McpTransport>;
6557
+ /** The resolved server + connection a transport should attach to. */
6558
+ interface McpServerRef {
6559
+ /** Server name (for diagnostics). */
6560
+ name: string;
6561
+ /** Remote endpoint, or undefined for a stdio server. */
6562
+ url?: string;
6563
+ /** Local stdio command + args, or undefined for an HTTP/SSE server. */
6564
+ command?: string;
6565
+ args?: string[];
6566
+ /** `'http'` (Streamable HTTP), `'sse'`, or `'stdio'` — resolved by the connector. */
6567
+ transport: 'http' | 'sse' | 'stdio';
6568
+ /** The registry connection id — keys the stored OAuth token (HTTP/SSE servers). */
6569
+ connectionId: string;
6570
+ }
6571
+
6572
+ /**
6573
+ * The direct MCP client transport — Lattice talks to an MCP server itself, on the
6574
+ * local machine, over Streamable HTTP (remote servers, per-server OAuth) or stdio
6575
+ * (a local server process, fully offline). This is the only production transport:
6576
+ * no data is routed through a cloud middleman.
6577
+ *
6578
+ * `@modelcontextprotocol/sdk` is an OPTIONAL dependency, lazy-imported with
6579
+ * non-literal specifiers so `latticesql` compiles + installs without it (and so
6580
+ * it resolves under both Node and the desktop Deno runtime). The SDK surface is
6581
+ * hand-typed at the seam, mirroring the Jira `jira.js` seam.
6582
+ */
6583
+
6584
+ /**
6585
+ * The transport factory used at SYNC time — the connection is already authorized,
6586
+ * so the stored token is attached and no redirect is expected. Opens a client,
6587
+ * ready for `listChanges` to call read tools.
6588
+ */
6589
+ declare function connectDirect(ref: McpServerRef): Promise<McpTransport>;
6590
+ interface BeginOAuthArgs {
6591
+ connectionId: string;
6592
+ serverUrl: string;
6593
+ redirectUri: string;
6594
+ state: string;
6595
+ /** `'http'` (Streamable HTTP) or `'sse'`. */
6596
+ transportKind: 'http' | 'sse';
6597
+ clientName?: string;
6598
+ scope?: string;
6599
+ }
6600
+ interface CompleteOAuthArgs {
6601
+ connectionId: string;
6602
+ redirectUri: string;
6603
+ code: string;
6604
+ /** `'http'` (Streamable HTTP) or `'sse'` — must match the begin call. */
6605
+ transportKind: 'http' | 'sse';
6606
+ clientName?: string;
6607
+ scope?: string;
6608
+ state?: string;
6609
+ }
6610
+
6611
+ /**
6612
+ * The base for every MCP-backed connector.
6613
+ *
6614
+ * A concrete connector (Gmail, Calendar, Jira, …) supplies only schema + mapping:
6615
+ * the connected {@link ConnectedModelDef}s (`models`), the MCP server(s) it reads
6616
+ * from (`mcpServers`), and, per model, which read tool feeds it and how to map the
6617
+ * tool's JSON into rows (`bindings`). Everything else — connecting (per-server
6618
+ * OAuth or a local stdio server), paging, and yielding {@link ExternalRecord}s to
6619
+ * the unchanged sync engine — lives here.
6620
+ *
6621
+ * The transport and OAuth driver are constructor seams so tests inject fakes and
6622
+ * never touch the network or the MCP SDK.
6623
+ */
6624
+
6625
+ /** Binds one connected model to the MCP read tool that feeds it, plus its mapper. */
6626
+ interface McpModelBinding {
6627
+ /** The connected model key (matches {@link ConnectedModelDef.model}). */
6628
+ model: string;
6629
+ /** The MCP read tool that feeds this model. */
6630
+ tool: string;
6631
+ /**
6632
+ * Build the tool arguments for one page. `parentKey` is set for per-parent
6633
+ * models (e.g. an issue key when fetching its comments); `cursor` is the page
6634
+ * token from {@link nextCursor} on the prior page (null on the first page).
6635
+ */
6636
+ buildArgs(ctx: {
6637
+ parentKey?: string;
6638
+ cursor?: string | null;
6639
+ }): Record<string, unknown>;
6640
+ /** Pull the array of raw items out of the tool's JSON result. */
6641
+ items(result: unknown): unknown[];
6642
+ /** Map one raw item to an {@link ExternalRecord}. Return null to skip it. */
6643
+ map(item: unknown, ctx: {
6644
+ parentKey?: string;
6645
+ }): ExternalRecord | null;
6646
+ /** Extract the next-page cursor, or a falsy value when the last page was reached. */
6647
+ nextCursor?(result: unknown): string | null | undefined;
6648
+ }
6649
+ /** OAuth driver seam — the real SDK-backed functions in production, fakes in tests. */
6650
+ interface McpOAuthDriver {
6651
+ begin(args: BeginOAuthArgs): Promise<{
6652
+ authorizationUrl: string | undefined;
6653
+ toolNames: string[];
6654
+ serverName?: string;
6655
+ }>;
6656
+ complete(args: CompleteOAuthArgs): Promise<{
6657
+ toolNames: string[];
6658
+ serverName?: string;
6659
+ }>;
6660
+ }
6661
+ declare abstract class McpConnectorBase implements McpConnector {
6662
+ private readonly transportFactory;
6663
+ private readonly oauth;
6664
+ protected constructor(transportFactory?: McpTransportFactory, oauth?: McpOAuthDriver);
6665
+ abstract readonly connector: string;
6666
+ abstract toolkits(): string[];
6667
+ abstract presentation(toolkit: string): ToolkitPresentation;
6668
+ abstract models(toolkit: string): ConnectedModelDef[];
6669
+ abstract mcpServers(toolkit: string): McpServerSpec[];
6670
+ /** The per-model tool + mapper bindings for a toolkit. */
6671
+ protected abstract bindings(toolkit: string): McpModelBinding[];
6672
+ /** Optional OAuth scope string requested for a toolkit's server. */
6673
+ protected scope(_toolkit: string): string | undefined;
6674
+ private resolveServer;
6675
+ private transportKind;
6676
+ private needsOAuth;
6677
+ private buildRef;
6678
+ /** Open a transport to this toolkit's server for an existing connection (sync time). */
6679
+ protected openServerTransport(toolkit: string, connectionId: string): Promise<McpTransport>;
6680
+ beginConnect(_userId: string, toolkit: string, opts?: {
6681
+ redirectUri?: string;
6682
+ serverUrl?: string;
6683
+ clientInfo?: {
6684
+ client_id: string;
6685
+ client_secret?: string;
6686
+ };
6687
+ targetConnectorId?: string;
6688
+ }): Promise<McpBeginResult>;
6689
+ completeConnect(pendingId: string, params: {
6690
+ code: string;
6691
+ state?: string;
6692
+ }): Promise<{
5477
6693
  connectionId: string;
5478
6694
  displayName: string | null;
6695
+ serverName?: string;
6696
+ targetConnectorId?: string;
5479
6697
  }>;
5480
- listChanges(toolkit: string, model: string, ctx: ListChangesContext): AsyncIterable<ExternalRecord>;
5481
6698
  disconnect(connectionId: string): Promise<void>;
5482
- /** Offset-paged fetch (startAt/total/isLast). Stops on isLast, exhausted total, or a short page. */
5483
- private pageOffset;
5484
- /** Token-paged fetch (nextPageToken) — the enhanced JQL issue search. */
5485
- private pageToken;
6699
+ purgeConnection(connectionId: string): Promise<void>;
6700
+ private readonly _activeSessions;
6701
+ private readonly _sessionTransports;
6702
+ /** Reuse ONE transport for this connection across the sync's models/parents. */
6703
+ beginSyncSession(connectionId: string): Promise<void>;
6704
+ /** Close + evict the shared transport opened during the session (idempotent). */
6705
+ endSyncSession(connectionId: string): Promise<void>;
6706
+ listChanges(toolkit: string, model: string, ctx: ListChangesContext): AsyncIterable<ExternalRecord>;
6707
+ authorize(_userId: string, _toolkit: string): Promise<AuthorizeResult>;
6708
+ completeAuth(_userId: string, _toolkit: string): Promise<ConnectionResult>;
6709
+ private displayNameFor;
6710
+ }
6711
+ /** A single-toolkit MCP connector defined purely by schema + bindings. */
6712
+ interface McpConnectorSpec {
6713
+ /** Connector id (also the toolkit id unless {@link toolkit} is set). */
6714
+ connector: string;
6715
+ /** Toolkit id, if it differs from the connector id. */
6716
+ toolkit?: string;
6717
+ presentation: ToolkitPresentation;
6718
+ servers: McpServerSpec[];
6719
+ models: ConnectedModelDef[];
6720
+ bindings: McpModelBinding[];
6721
+ /** OAuth scope requested for the server, if any. */
6722
+ scope?: string;
6723
+ }
6724
+ /** Test/DI seams for a {@link SimpleMcpConnector}. */
6725
+ interface McpConnectorDeps {
6726
+ transportFactory?: McpTransportFactory;
6727
+ oauth?: McpOAuthDriver;
6728
+ }
6729
+ /**
6730
+ * The concrete connector every built-in MCP connector module instantiates: it
6731
+ * wires a {@link McpConnectorSpec} into the base, so a connector module is just
6732
+ * its table schema + per-model tool bindings — no class boilerplate.
6733
+ */
6734
+ declare class SimpleMcpConnector extends McpConnectorBase {
6735
+ private readonly spec;
6736
+ readonly connector: string;
6737
+ private readonly toolkit;
6738
+ constructor(spec: McpConnectorSpec, deps?: McpConnectorDeps);
6739
+ toolkits(): string[];
6740
+ presentation(_toolkit: string): ToolkitPresentation;
6741
+ models(_toolkit: string): ConnectedModelDef[];
6742
+ mcpServers(_toolkit: string): McpServerSpec[];
6743
+ protected bindings(_toolkit: string): McpModelBinding[];
6744
+ protected scope(_toolkit: string): string | undefined;
5486
6745
  }
5487
6746
 
5488
6747
  /**
5489
- * Jira connected data types — the six tables the Jira connector syncs.
5490
- *
5491
- * Pure schema: each model's Lattice {@link TableDefinition} (with a `source`
5492
- * descriptor), its natural key (a stable Jira id/key = the primary key, so
5493
- * re-syncs upsert idempotently), and the FK relations that derive graph edges.
5494
- * The fetch/map logic lives in the connector ({@link JiraConnector}), which calls
5495
- * the Jira REST/Agile API directly via `jira.js` — there is no broker.
6748
+ * Shared builder for MCP-connector connected models — the same "Jira data
6749
+ * conventions" every connector reuses: a Lattice {@link TableDefinition} with the
6750
+ * standard lifecycle columns, a `source` descriptor (per-member `private`
6751
+ * visibility), an `outputFile`, and optional graph edges + a per-parent binding.
5496
6752
  */
5497
6753
 
5498
- /** All six Jira connected models, in dependency order (parents before children). */
5499
- declare const JIRA_MODELS: ConnectedModelDef[];
6754
+ interface McpModelArgs {
6755
+ connector: string;
6756
+ toolkit: string;
6757
+ table: string;
6758
+ model: string;
6759
+ naturalKey: string;
6760
+ columns: Record<string, string>;
6761
+ def: Partial<TableDefinition>;
6762
+ graphEdges?: ConnectedModelDef['graphEdges'];
6763
+ parent?: ConnectedModelDef['parent'];
6764
+ }
6765
+ /** Build a {@link ConnectedModelDef} for an MCP connector (mirrors `jira/models.ts`). */
6766
+ declare function mcpModel(args: McpModelArgs): ConnectedModelDef;
6767
+
5500
6768
  /**
5501
- * Define the six Jira connected tables on a live database (post-init), skipping
5502
- * any that already exist. Called before the first sync / on connector setup.
6769
+ * Persisted + memoized schema descriptor for one MCP connection the MCP analogue of
6770
+ * `db-source/schema-cache.ts`. The connector SPI's `models()` is SYNCHRONOUS, but discovering
6771
+ * an MCP server's record shapes means calling its read tools (async), so introspection happens
6772
+ * in the async connect/refresh path, persists a descriptor in the machine-local encrypted store
6773
+ * (keyed `mcp_schema:<connectionId>`), and `models()` reads it back synchronously via
6774
+ * {@link buildMcpModelDefs}. That turns a server's flat item stream into one TYPED table per
6775
+ * record kind (e.g. `deduction_types`, `company`), each namespaced per connection so two servers
6776
+ * never collide and each auto-groups under its own GUI schema header (via `mcp:<connId>` toolkit).
5503
6777
  */
5504
- declare function defineJiraTables(db: Lattice): Promise<void>;
6778
+
6779
+ /** SQL spec inferred for a modeled column. */
6780
+ type McpSqlSpec = 'TEXT' | 'INTEGER' | 'REAL';
6781
+ interface McpColumnDesc {
6782
+ name: string;
6783
+ sqlSpec: McpSqlSpec;
6784
+ }
6785
+ /** One record kind discovered on the server → one typed Lattice table. */
6786
+ interface McpKindDesc {
6787
+ /** Record kind slug (the table's identity within the connection), e.g. `deduction_types`. */
6788
+ kind: string;
6789
+ /** The server read tool whose items populate this kind (e.g. `list_deduction_types`). */
6790
+ tool: string;
6791
+ /** Modeled columns inferred from sampled items (excludes the natural key + lifecycle cols). */
6792
+ columns: McpColumnDesc[];
6793
+ /** The item field used as the natural key, or `_pk` when synthesized (no stable id field). */
6794
+ naturalKey: string;
6795
+ }
6796
+ interface McpSchemaDescriptor {
6797
+ /** Sanitized slug namespacing this connection's tables (from the server brand / display name). */
6798
+ prefix: string;
6799
+ kinds: McpKindDesc[];
6800
+ }
5505
6801
 
5506
6802
  /**
5507
- * The Trello connector — a {@link Connector} that talks to Trello's REST API
5508
- * DIRECTLY over the Node global `fetch`, authenticated with the user's OWN API
5509
- * key + token (passed as `?key=&token=` query params). No broker, no SDK, no
5510
- * extra dependency.
6803
+ * The introspective MCP connector — the always-works path.
6804
+ *
6805
+ * Point it at ANY reachable MCP server (a URL you supply, or a local stdio
6806
+ * command) and it pulls that server's readable items in as context without a
6807
+ * hand-authored schema: it introspects the server's tools (`tools/list`), calls
6808
+ * each no-argument read tool, and ALSO lists the server's advertised resources
6809
+ * (`resources/list` — its "available files"). Everything lands in one connected
6810
+ * `*_items` table (typed columns for kind/tool/server/title/summary + a JSON
6811
+ * `data` blob, FTS on title/summary, per-member `private` visibility — the same
6812
+ * connector-table conventions the sync engine expects).
5511
6813
  *
5512
- * The SPI is OAuth-shaped (authorize→redirect→completeAuth); Trello uses direct
5513
- * credentials instead, so `authorize`/`completeAuth` are not part of its flow —
5514
- * the GUI collects the API key + token and calls {@link TrelloConnector.connect},
5515
- * which validates them against Trello and stores them encrypted. `listChanges`
5516
- * (sync) and `disconnect` (teardown) satisfy the SPI normally, keyed by the
5517
- * stored connection. The schema (the eleven connected models) lives in
5518
- * `./models.ts`; this file is the fetch/auth half.
6814
+ * Two uses:
6815
+ * - {@link genericConnector} the built-in bring-your-own-URL connector; every
6816
+ * added server is its own connection (one registry row per server).
6817
+ * - {@link introspectiveConnector} the same engine pre-pointed at a fixed
6818
+ * endpoint with its own table name, for library consumers that embed a
6819
+ * specific provider.
6820
+ *
6821
+ * Only read-shaped tools are called: tools that require arguments are skipped in
6822
+ * introspective mode, and obvious write tools are never called.
5519
6823
  */
5520
6824
 
5521
- /** The user's Trello credentials for one connection. */
5522
- interface TrelloCreds {
5523
- /** Trello API key (from trello.com/power-ups/admin). */
5524
- apiKey: string;
5525
- /** Trello API token authorizing access for this key — a user secret. */
5526
- token: string;
6825
+ /** Configuration for an introspective connector instance. */
6826
+ interface IntrospectiveSpec {
6827
+ /** Connector + toolkit id. */
6828
+ connector: string;
6829
+ /** Display label. */
6830
+ label: string;
6831
+ /** Badge letter + colour for the fallback icon. */
6832
+ iconLetter: string;
6833
+ iconColor: string;
6834
+ /** The connected table name (e.g. `'mcp_items'`, `'jira_items'`). */
6835
+ table: string;
6836
+ /** The MCP server(s). A branded connector sets a default url; generic leaves it blank. */
6837
+ servers: McpServerSpec[];
5527
6838
  }
5528
- /** Read the stored credentials for a connection, or null. */
5529
- declare function getTrelloCreds(connectionId: string): TrelloCreds | null;
5530
- /** Persist credentials for a connection to the machine-local encrypted store. */
5531
- declare function setTrelloCreds(connectionId: string, creds: TrelloCreds): void;
5532
- /** Remove the stored credentials for a connection. */
5533
- declare function clearTrelloCreds(connectionId: string): void;
5534
- type Json = Record<string, unknown>;
5535
- /** Options for the paged card/comment fetches (Trello's `before` cursor). */
5536
- interface TrelloPageOpts {
5537
- /** Max rows to return this page. */
5538
- limit: number;
5539
- /** Return only rows older than this id (Trello returns newest-first). */
5540
- before?: string;
5541
- }
5542
- /** The minimal Trello surface the connector depends on (all calls return raw REST shapes). */
5543
- interface TrelloClient {
5544
- /** Validate the credentials + return the authenticated member (`GET /members/me`). */
5545
- me(): Promise<Json>;
5546
- /** The authenticated member's boards (`GET /members/me/boards`). */
5547
- myBoards(): Promise<Json[]>;
5548
- /** A board's lists (`GET /boards/{id}/lists`). */
5549
- boardLists(boardId: string): Promise<Json[]>;
5550
- /** A board's members (`GET /boards/{id}/members`). */
5551
- boardMembers(boardId: string): Promise<Json[]>;
5552
- /** A board's labels (`GET /boards/{id}/labels`). */
5553
- boardLabels(boardId: string): Promise<Json[]>;
5554
- /** A board's cards, paged newest-first via `before` (`GET /boards/{id}/cards`). */
5555
- boardCards(boardId: string, opts: TrelloPageOpts): Promise<Json[]>;
5556
- /** A card's `commentCard` actions, paged via `before` (`GET /cards/{id}/actions`). */
5557
- cardComments(cardId: string, opts: TrelloPageOpts): Promise<Json[]>;
5558
- /** A card's assigned members (`GET /cards/{id}/members`). */
5559
- cardMembers(cardId: string): Promise<Json[]>;
5560
- /** A card's applied labels (`GET /cards/{id}/labels`). */
5561
- cardLabels(cardId: string): Promise<Json[]>;
5562
- /** A card's checklists (`GET /cards/{id}/checklists`). */
5563
- cardChecklists(cardId: string): Promise<Json[]>;
5564
- /** A checklist's items (`GET /checklists/{id}/checkItems`). */
5565
- checklistItems(checklistId: string): Promise<Json[]>;
5566
- }
5567
- /**
5568
- * Build a {@link TrelloClient} that talks to Trello over the Node global `fetch`,
5569
- * authenticated with the user's API key + token. No SDK, no extra dependency.
5570
- */
5571
- declare function loadTrelloClient(creds: TrelloCreds): Promise<TrelloClient>;
5572
- declare class TrelloConnector implements CredentialConnector {
5573
- private readonly clientFactory;
5574
- private readonly credsLoader;
5575
- readonly connector = "trello";
6839
+ declare class IntrospectiveMcpConnector extends McpConnectorBase {
6840
+ private readonly spec;
6841
+ readonly connector: string;
6842
+ private readonly model;
6843
+ constructor(spec: IntrospectiveSpec, deps?: McpConnectorDeps);
6844
+ toolkits(): string[];
6845
+ presentation(_toolkit: string): ToolkitPresentation;
6846
+ models(toolkit: string): ConnectedModelDef[];
6847
+ mcpServers(_toolkit: string): McpServerSpec[];
6848
+ protected bindings(_toolkit: string): McpModelBinding[];
5576
6849
  /**
5577
- * @param clientFactory builds a {@link TrelloClient} from creds (default: the
5578
- * real `fetch`-backed client; tests inject a fake).
5579
- * @param credsLoader resolves a connection id to its stored creds (default: the
5580
- * machine-local store; tests inject a fake).
6850
+ * Discover the server's record shapes and persist a typed schema descriptor: call each
6851
+ * non-write read tool once, infer a kind + typed columns from a sample, and store it under
6852
+ * the connection. `models()` then emits one typed table per kind. Best-effort — returns null
6853
+ * when the server exposes nothing modelable (the caller keeps the flat `mcp_items` fallback).
6854
+ * `prefix` namespaces the tables (the server brand, e.g. `justworks`).
5581
6855
  */
5582
- constructor(clientFactory?: (creds: TrelloCreds) => Promise<TrelloClient>, credsLoader?: (connectionId: string) => TrelloCreds | null);
6856
+ introspect(connectionId: string, toolkit: string, prefix: string): Promise<McpSchemaDescriptor | null>;
6857
+ listChanges(toolkit: string, model: string, ctx: ListChangesContext): AsyncIterable<ExternalRecord>;
6858
+ }
6859
+ /** Build an introspective connector for a specific provider (branded, pre-pointed). */
6860
+ declare function introspectiveConnector(spec: IntrospectiveSpec, deps?: McpConnectorDeps): IntrospectiveMcpConnector;
6861
+ /** The generic MCP connector — point it at any reachable MCP server (no default url). */
6862
+ declare function genericConnector(deps?: McpConnectorDeps): IntrospectiveMcpConnector;
6863
+
6864
+ /**
6865
+ * The external-database connector — a {@link CredentialConnector} that connects to
6866
+ * an external Postgres-family database (AWS RDS Postgres, Supabase, generic
6867
+ * Postgres) the user supplies via host/user/password fields (read-only by
6868
+ * contract — see external-pool.ts; raw connection strings are not accepted), and
6869
+ * IMPORTS its tables into Lattice as connected data types (so they land in the
6870
+ * SOURCE·INPUTS tier and sync via the shared connector sync engine).
6871
+ *
6872
+ * Unlike single-connection connectors (Jira/Trello), each external DB is its own
6873
+ * connection with its own table set, so the toolkit string is per-connection
6874
+ * (`db_source:<connectionId>`) and the dedicated `/api/db-sources` route creates a
6875
+ * new registry row per connection. The credentials (connection string) + the
6876
+ * introspected schema descriptor are persisted in the machine-local encrypted
6877
+ * store — never in the registry, responses, or logs.
6878
+ */
6879
+
6880
+ /** Read the stored connection string for a connection, or null. */
6881
+ declare function getDbSourceCreds(connectionId: string): string | null;
6882
+ /** Persist the connection string (machine-local, encrypted). */
6883
+ declare function setDbSourceCreds(connectionId: string, connectionString: string): void;
6884
+ /** Remove the stored connection string. */
6885
+ declare function clearDbSourceCreds(connectionId: string): void;
6886
+ /**
6887
+ * Assemble a Postgres connection string from host + user + database (+ optional
6888
+ * port/password). Raw connection strings are deliberately NOT accepted: pasting a
6889
+ * full URL invites reusing an owner/admin connection wholesale, and the read-only
6890
+ * data-source contract wants the credentials entered deliberately (ideally a
6891
+ * read-only database user). Throws loudly when incomplete — never a silent default.
6892
+ */
6893
+ declare function assembleConnectionString(creds: Record<string, string>): string;
6894
+ declare class DatabaseConnector implements CredentialConnector {
6895
+ private readonly credsLoader;
6896
+ readonly connector = "db_source";
6897
+ /** @param credsLoader test seam — resolve a connection id to its connection string. */
6898
+ constructor(credsLoader?: (connectionId: string) => string | null);
5583
6899
  toolkits(): string[];
5584
6900
  models(toolkit: string): ConnectedModelDef[];
5585
- presentation(toolkit: string): ToolkitPresentation;
6901
+ presentation(_toolkit: string): ToolkitPresentation;
5586
6902
  credentialFields(): CredentialField[];
5587
- helpUrl(): string;
5588
- /**
5589
- * Trello authenticates with a direct API key + token, not an OAuth redirect.
5590
- * The GUI collects them and calls {@link connect}; these SPI methods are
5591
- * therefore not part of the Trello flow and reject with a clear error.
5592
- */
6903
+ helpUrl(): string | undefined;
5593
6904
  authorize(_userId: string, _toolkit: string): Promise<AuthorizeResult>;
5594
6905
  completeAuth(_userId: string, _toolkit: string): Promise<ConnectionResult>;
5595
6906
  /**
5596
- * Validate Trello credentials (`GET /members/me`) and, on success, store them
5597
- * encrypted under a fresh connection id. Returns the connection id (recorded in
5598
- * the registry by the caller) and the validated member's full name (for the
5599
- * UI). Throws if the credentials are invalid or absent.
6907
+ * Validate the credentials against the external DB (`SELECT 1`), introspect its
6908
+ * schema, and persist both the connection string + schema descriptor encrypted
6909
+ * under a fresh connection id. Returns the id (recorded in the registry by the
6910
+ * caller) and the database name as the display name.
5600
6911
  */
5601
6912
  connect(creds: Record<string, string>): Promise<{
5602
6913
  connectionId: string;
5603
6914
  displayName: string | null;
5604
6915
  }>;
5605
- listChanges(toolkit: string, model: string, ctx: ListChangesContext): AsyncIterable<ExternalRecord>;
5606
- disconnect(connectionId: string): Promise<void>;
5607
6916
  /**
5608
- * Cursor-paged fetch for Trello's newest-first list endpoints: pass the oldest
5609
- * id seen on the previous page as `before` to walk backwards. Stops on a short
5610
- * page; throws past {@link MAX_PAGES} to avoid an unbounded loop.
6917
+ * Re-authenticate an EXISTING connection with edited credentials (e.g. a rotated
6918
+ * password or a corrected host/port). Unlike {@link connect}, this reuses the
6919
+ * connection id AND the descriptor's original table prefix, so the imported
6920
+ * tables keep their physical names and rows upsert idempotently onto the same
6921
+ * objects — an edit re-points the same database, it does not fork a new table
6922
+ * set. Validates against the DB, re-introspects (picking up any newly-added
6923
+ * tables), and overwrites the stored connection string + schema descriptor.
5611
6924
  */
5612
- private pageBefore;
6925
+ reconnect(connectionId: string, creds: Record<string, string>): Promise<{
6926
+ connectionId: string;
6927
+ displayName: string | null;
6928
+ }>;
6929
+ listChanges(toolkit: string, model: string, ctx: ListChangesContext): AsyncIterable<ExternalRecord>;
6930
+ disconnect(connectionId: string): Promise<void>;
5613
6931
  }
5614
6932
 
5615
6933
  /**
5616
- * Trello connected data types the eleven tables the Trello connector syncs.
5617
- *
5618
- * Pure schema: each model's Lattice {@link TableDefinition} (with a `source`
5619
- * descriptor), its natural key (a stable Trello id = the primary key, so re-syncs
5620
- * upsert idempotently), and the FK relations that derive graph edges. The
5621
- * fetch/map logic lives in the connector ({@link TrelloConnector}), which calls
5622
- * the Trello REST API directly over the Node global `fetch` — there is no broker
5623
- * and no SDK dependency.
6934
+ * SQL dialects for the external-database connector. A dialect knows how to (a)
6935
+ * introspect a schema (tables → columns → primary keys), (b) map a native column
6936
+ * type to a Lattice column spec, and (c) build a bounded page query. v5.0 ships
6937
+ * Postgres-family support only; the interface is structured so MySQL/Snowflake can
6938
+ * slot in later without touching the connector.
5624
6939
  *
5625
- * Trello's members-on-a-board and members/labels-on-a-card relationships are
5626
- * many-to-many, and `graphEdges` derives one edge per single FK column. So those
5627
- * relationships are modeled as junction tables (`trello_board_members`,
5628
- * `trello_card_members`, `trello_card_labels`) whose composite-key rows each
5629
- * carry the two FK columns that produce real graph edges. A flat
5630
- * `trello_members` identity table holds member profiles (Trello has no global
5631
- * member list, so members are discovered per board).
6940
+ * All introspection/page SQL is parameterized or built from identifiers this
6941
+ * module quotes never string-interpolated user values.
6942
+ */
6943
+ /** A SQL fragment plus its bound parameters (Postgres `$1` placeholders). */
6944
+ interface SqlQuery {
6945
+ sql: string;
6946
+ params: unknown[];
6947
+ }
6948
+ /** Options for a bounded page over one external table. */
6949
+ interface PageOpts {
6950
+ schema: string;
6951
+ table: string;
6952
+ /** Explicitly projected columns (never `SELECT *` — bounded reads). */
6953
+ columns: string[];
6954
+ /** Single-column key to keyset-paginate on, or null to offset-paginate. */
6955
+ keyCol: string | null;
6956
+ /** Last key value seen (keyset only); undefined for the first page. */
6957
+ afterKey?: unknown;
6958
+ /** Row offset (offset pagination only). */
6959
+ offset: number;
6960
+ /** Page size. */
6961
+ limit: number;
6962
+ }
6963
+ interface SqlDialect {
6964
+ readonly id: 'postgres';
6965
+ /** True when this dialect handles the given connection string. */
6966
+ detect(connectionString: string): boolean;
6967
+ /** Quote an identifier (table/column/schema) for this dialect. */
6968
+ quoteIdent(id: string): string;
6969
+ /** List base tables in a schema → rows `{ name }`. */
6970
+ tablesSql(schema: string): SqlQuery;
6971
+ /** All columns for a schema → rows `{ table, name, type }` (ordinal order). */
6972
+ columnsSql(schema: string): SqlQuery;
6973
+ /** Primary-key columns for a schema → rows `{ table, col }` (key order). */
6974
+ primaryKeysSql(schema: string): SqlQuery;
6975
+ /**
6976
+ * FOREIGN KEY columns for a schema → rows `{ cname, table, col, ref_table,
6977
+ * ref_col }`. May emit multiple rows per composite constraint — the caller
6978
+ * groups by `cname` and keeps single-column FKs only (those are the ones that
6979
+ * map cleanly onto graph edges).
6980
+ */
6981
+ foreignKeysSql(schema: string): SqlQuery;
6982
+ /** Map a native column type to a Lattice column SQL spec (TEXT | INTEGER | REAL). */
6983
+ mapType(nativeType: string): 'TEXT' | 'INTEGER' | 'REAL';
6984
+ /** Build a bounded page query (keyset when keyCol set, else offset). */
6985
+ pageSql(opts: PageOpts): SqlQuery;
6986
+ }
6987
+ /** Postgres-family dialect (AWS RDS Postgres, Supabase, generic Postgres). */
6988
+ declare const PostgresDialect: SqlDialect;
6989
+ /**
6990
+ * Resolve the dialect for a connection string. v5.0 supports Postgres-family
6991
+ * only; a non-Postgres URL throws a clear, actionable error — never a silent
6992
+ * fallback.
6993
+ */
6994
+ declare function dialectFor(connectionString: string): SqlDialect;
6995
+
6996
+ /**
6997
+ * Persisted + memoized schema descriptor for one external-DB connection. The
6998
+ * connector's `models()` is SYNCHRONOUS (the SPI + sync engine call it inline),
6999
+ * but introspecting an external DB is async — so introspection happens in the
7000
+ * async `connect()`/refresh paths, persists the descriptor in the machine-local
7001
+ * encrypted store (alongside the credentials), and `models()` reads it back
7002
+ * synchronously here. No schema migration: this rides the existing encrypted
7003
+ * credential store, not the registry table.
5632
7004
  */
5633
7005
 
5634
- /** All eleven Trello connected models, in dependency order (parents before children). */
5635
- declare const TRELLO_MODELS: ConnectedModelDef[];
7006
+ interface DbColumnDesc {
7007
+ /** External column name. */
7008
+ name: string;
7009
+ /** Mapped Lattice column spec. */
7010
+ sqlSpec: 'TEXT' | 'INTEGER' | 'REAL';
7011
+ }
7012
+ interface DbTableDesc {
7013
+ /** External table name within the source schema. */
7014
+ name: string;
7015
+ columns: DbColumnDesc[];
7016
+ /** Primary-key columns in key order (may be empty for a keyless table). */
7017
+ pk: string[];
7018
+ /** Whether this table is imported (table-level selection). */
7019
+ selected: boolean;
7020
+ /**
7021
+ * Single-column FOREIGN KEYs introspected from the remote (composite FKs are
7022
+ * dropped at introspection time). Materialized as graph edges between the
7023
+ * imported tables so a connected database's relational structure carries over.
7024
+ * Optional — descriptors persisted before FK introspection existed lack it.
7025
+ */
7026
+ fks?: {
7027
+ column: string;
7028
+ refTable: string;
7029
+ refColumn: string;
7030
+ }[];
7031
+ }
7032
+ interface DbSchemaDescriptor {
7033
+ dialect: string;
7034
+ /** Source schema (e.g. `public`). */
7035
+ schema: string;
7036
+ /** Sanitized slug namespacing the imported Lattice tables (from the display name). */
7037
+ prefix: string;
7038
+ tables: DbTableDesc[];
7039
+ }
7040
+ declare function getSchemaDescriptor(connectionId: string): DbSchemaDescriptor | null;
7041
+ declare function setSchemaDescriptor(connectionId: string, descriptor: DbSchemaDescriptor): void;
7042
+ declare function clearSchemaDescriptor(connectionId: string): void;
7043
+ /** Lowercase, non-alphanumeric→underscore slug, bounded; never empty. */
7044
+ declare function slugify(name: string): string;
7045
+ /** The imported Lattice table name for an external table (namespaced by prefix). */
7046
+ declare function latticeTableName(prefix: string, externalTable: string): string;
5636
7047
  /**
5637
- * Define the eleven Trello connected tables on a live database (post-init),
5638
- * skipping any that already exist. Called before the first sync / on connector
5639
- * setup.
7048
+ * Build the Lattice {@link ConnectedModelDef}s for the selected tables. Pure +
7049
+ * synchronous so `models()` can return immediately. Each table maps to a Lattice
7050
+ * table carrying the standard lifecycle/lineage columns + the external columns,
7051
+ * with a `source` descriptor (so the row stamps land it in the SOURCE·INPUTS tier).
5640
7052
  */
5641
- declare function defineTrelloTables(db: Lattice): Promise<void>;
7053
+ declare function buildModelDefs(connectionId: string, descriptor: DbSchemaDescriptor): ConnectedModelDef[];
5642
7054
 
5643
7055
  /**
5644
7056
  * Connector sync engine.
@@ -5774,6 +7186,21 @@ declare function enableConnectorRls(db: Lattice, connector: Connector, toolkit:
5774
7186
  */
5775
7187
  declare function secureConnectorTables(db: Lattice, connector: Connector): Promise<void>;
5776
7188
 
7189
+ /**
7190
+ * A compact summary of the workspace's connected external sources for the
7191
+ * assistant's context, so it can answer "are you connected to X?" and knows
7192
+ * which table holds each source's data. Returns '' when nothing is connected.
7193
+ *
7194
+ * On a CLOUD (Postgres) workspace this is scoped by `connectedBy` so a member
7195
+ * never sees another member's connections. On a LOCAL (SQLite) single-user
7196
+ * workspace the `connected_by` stamp is meaningless and can drift when the
7197
+ * user's saved identity changes (a connector stamped with an old email would
7198
+ * then be invisible), so every connection is listed — matching how the sidebar
7199
+ * re-registers connector tables locally. This is the same trust boundary: a
7200
+ * local workspace is one user's own machine.
7201
+ */
7202
+ declare function describeConnectedSources(db: Lattice, connectedBy: string): Promise<string>;
7203
+
5777
7204
  /**
5778
7205
  * Durable retry for transient database failures.
5779
7206
  *
@@ -6134,6 +7561,11 @@ interface UserIdentity {
6134
7561
  * Read the machine-local user identity. Returns `{display_name: '',
6135
7562
  * email: ''}` if the file is missing or malformed — callers can treat
6136
7563
  * empty fields as "not set yet" without a separate existence check.
7564
+ *
7565
+ * In a managed/hosted deployment there is no local identity file, so a stored
7566
+ * field falls back to the `LATTICE_USER_NAME` / `LATTICE_USER_EMAIL` env vars
7567
+ * (which the host injects per session). Env is only a fallback — a value written
7568
+ * to the identity file always wins, preserving the "empty = not set" contract.
6137
7569
  */
6138
7570
  declare function readIdentity(): UserIdentity;
6139
7571
  /**
@@ -6169,6 +7601,15 @@ interface UserPreferences {
6169
7601
  * A user preference, machine-local (see `voice_provider`).
6170
7602
  */
6171
7603
  aggressiveness: number;
7604
+ /**
7605
+ * Clarify threshold (0..1) — the single confidence bar that decides when an
7606
+ * automated inference asks the user instead of guessing. At or above the
7607
+ * threshold the system acts silently; between the floor (threshold / 2,
7608
+ * derived where consumed) and the threshold it asks a short multiple-choice
7609
+ * question; below the floor it drops the inference as noise. A user
7610
+ * preference, machine-local (see `voice_provider`).
7611
+ */
7612
+ clarify_threshold: number;
6172
7613
  }
6173
7614
  /**
6174
7615
  * Read machine-local user preferences. Returns defaults if the file is
@@ -6628,14 +8069,14 @@ declare function hasVectorIndex(adapter: StorageAdapter, table: string): Promise
6628
8069
  * true — surfacing a misconfiguration loudly rather than silently doing
6629
8070
  * nothing. By default it is a reported no-op (returns 0).
6630
8071
  */
6631
- declare function buildVectorIndex(adapter: StorageAdapter, table: string, dim: number, requireExtension?: boolean): Promise<number>;
6632
- /** Drop a table's native vector index. */
8072
+ declare function buildVectorIndex(adapter: StorageAdapter, table: string, dim: number, requireExtension?: boolean, opts?: VectorIndexOptions): Promise<number>;
8073
+ /** Drop a table's native vector index (and its registry row). */
6633
8074
  declare function dropVectorIndex(adapter: StorageAdapter, table: string): Promise<void>;
6634
8075
  /**
6635
8076
  * Query the native vector index for the nearest chunks to `queryVector`.
6636
8077
  * Returns up to `limit` hits with cosine similarity scores ≥ `minScore`.
6637
8078
  */
6638
- declare function searchVectorIndex(adapter: StorageAdapter, table: string, queryVector: number[], limit: number, minScore: number): Promise<VectorHit[]>;
8079
+ declare function searchVectorIndex(adapter: StorageAdapter, table: string, queryVector: number[], limit: number, minScore: number, efSearch?: number): Promise<VectorHit[]>;
6639
8080
 
6640
8081
  /**
6641
8082
  * Reference-ingestion API — parallel to {@link attachBlob}, but records a row
@@ -6907,6 +8348,16 @@ declare function memberRoleName(label: string): string;
6907
8348
  * `postgres://<role>:<password>@<host>/<db>` and sees only its permitted rows.
6908
8349
  */
6909
8350
  declare function provisionMemberRole(db: Lattice, role: string, password: string): Promise<void>;
8351
+ /**
8352
+ * Idempotently grant an EXISTING scoped login role membership in the cloud's
8353
+ * member group. The group holds every table/schema/connect grant and RLS keys on
8354
+ * `session_user`, so the role gets exactly an invited member's access while
8355
+ * staying RLS-confined to its own rows. Use this to adopt a role created out of
8356
+ * band (e.g. by an external provisioner) without rotating its password or
8357
+ * re-running CREATE/ALTER ROLE. The member group must already exist (it is created
8358
+ * when the owner first opens/secures the cloud).
8359
+ */
8360
+ declare function grantMemberAccess(db: Lattice, role: string): Promise<void>;
6910
8361
  /**
6911
8362
  * Change a row's sharing through the owner-only `lattice_set_row_visibility`
6912
8363
  * SECURITY DEFINER function. Only the row's owner (Postgres raises for anyone
@@ -7254,6 +8705,9 @@ interface ClaudeAuth {
7254
8705
  apiKey?: string | undefined;
7255
8706
  authToken?: string | undefined;
7256
8707
  betaHeader?: string | undefined;
8708
+ /** Override the Anthropic API host (the SDK's `baseURL`). Needed for a BYO custom-host key
8709
+ * or a proxy; when unset the SDK reads `process.env.ANTHROPIC_BASE_URL` or the default host. */
8710
+ baseURL?: string | undefined;
7257
8711
  }
7258
8712
 
7259
8713
  /** Generate a 1-2 sentence description of a document's text. */
@@ -7291,6 +8745,20 @@ interface ExtractedObject {
7291
8745
  values: Record<string, string>;
7292
8746
  /** Short human label for the object. */
7293
8747
  label: string;
8748
+ /**
8749
+ * The model's 0-1 confidence in its target-entity decision (that {@link entity}
8750
+ * is where these records belong / that a new entity is warranted). Optional:
8751
+ * a model that omits it is treated by consumers as fully confident (1.0), so
8752
+ * pre-confidence outputs behave exactly as before.
8753
+ */
8754
+ confidence?: number;
8755
+ /**
8756
+ * Labels of the OTHER extracted objects in the same document this object is
8757
+ * related to — e.g. a meeting lists its attendees' labels. Consumers materialize
8758
+ * these as record-to-record links (so a meeting links to its people, not just to
8759
+ * the source). Optional: absent/empty → no cross-object links.
8760
+ */
8761
+ links?: string[];
7294
8762
  }
7295
8763
  /** Parse + sanitize the extractor's JSON. Caps at 3 objects; drops invalid ones. */
7296
8764
  declare function parseObjects(raw: string): ExtractedObject[];
@@ -7471,6 +8939,9 @@ interface VisionOptions {
7471
8939
  prompt?: string;
7472
8940
  /** Cap on the normalized JPEG size (bytes). Default ~1.4 MB. */
7473
8941
  maxBytes?: number;
8942
+ /** The image's original media type (e.g. `image/png`). Used for the sharp-free fallback when
8943
+ * native normalization is unavailable — we send the raw bytes with this exact media type. */
8944
+ mediaType?: string;
7474
8945
  /** Injectable model call (test seam). Defaults to a real Anthropic vision call. */
7475
8946
  sender?: (input: VisionSenderInput) => Promise<string>;
7476
8947
  }
@@ -7495,8 +8966,14 @@ interface PdfOptions {
7495
8966
  */
7496
8967
  declare function describePdf(auth: ClaudeAuth, path: string, opts?: PdfOptions): Promise<string>;
7497
8968
 
7498
- /** How the running copy of the package was installed. */
7499
- type InstallKind = 'global' | 'local' | 'npx' | 'linked-dev' | 'unknown';
8969
+ /**
8970
+ * How the running copy of the package was installed. `desktop` is the bundled
8971
+ * desktop app (a compiled binary, not an npm install): it is never
8972
+ * `installable` via npm, but it CAN update itself through its own bundled
8973
+ * updater, so the GUI surfaces a "restart to update" action for it rather than
8974
+ * the npm "upgrade in place" action.
8975
+ */
8976
+ type InstallKind = 'global' | 'local' | 'npx' | 'linked-dev' | 'unknown' | 'desktop';
7500
8977
  interface InstallContext {
7501
8978
  kind: InstallKind;
7502
8979
  /** True only when an `npm install` may safely upgrade this copy in place. */
@@ -7514,6 +8991,19 @@ interface UpdateStatus {
7514
8991
  latest: string | null;
7515
8992
  kind: InstallContext['kind'];
7516
8993
  installable: boolean;
8994
+ /** Master switch — false when auto-update is disabled for this server. */
8995
+ autoUpdate: boolean;
8996
+ /**
8997
+ * What the user can DO about an available update on THIS surface:
8998
+ * - `upgrade-in-place`: an npm install can upgrade this copy now (the GUI's
8999
+ * manual "Upgrade" fallback to the background poll).
9000
+ * - `restart-to-update`: a newer version exists and the bundled desktop
9001
+ * updater can apply it on relaunch ("Restart to update").
9002
+ * - `none`: nothing to offer — already current, auto-update disabled, or a
9003
+ * surface that can't self-update (a dev/linked checkout, where `latest` is
9004
+ * still reported for information but there is no apply action).
9005
+ */
9006
+ action: 'upgrade-in-place' | 'restart-to-update' | 'none';
7517
9007
  checking: boolean;
7518
9008
  installing: boolean;
7519
9009
  lastError: string | null;
@@ -7526,6 +9016,80 @@ interface UpdateService {
7526
9016
  checkNow(force?: boolean): Promise<UpdateStatus>;
7527
9017
  }
7528
9018
 
9019
+ /**
9020
+ * Server-Sent Events protocol shared by the assistant chat stream and the
9021
+ * activity feed. The server writes `data: <json>\n\n` frames; the browser
9022
+ * reads them with a `fetch` ReadableStream reader. Both ends agree on the
9023
+ * event shapes defined here so neither side has to guess.
9024
+ */
9025
+ /** Events emitted while streaming one assistant turn (with tool calls). */
9026
+ type ChatStreamEvent = {
9027
+ type: 'assistant_message_start';
9028
+ id: string;
9029
+ } | {
9030
+ type: 'text_delta';
9031
+ delta: string;
9032
+ } | {
9033
+ type: 'tool_use';
9034
+ id: string;
9035
+ name: string;
9036
+ } | {
9037
+ type: 'tool_result';
9038
+ toolUseId: string;
9039
+ isError: boolean;
9040
+ } | {
9041
+ type: 'open';
9042
+ table: string;
9043
+ id: string;
9044
+ } | {
9045
+ type: 'question';
9046
+ question: string;
9047
+ options: string[];
9048
+ allowOther: boolean;
9049
+ } | {
9050
+ type: 'assistant_message_end';
9051
+ hadTools?: boolean;
9052
+ } | {
9053
+ type: 'ack';
9054
+ message: string;
9055
+ } | {
9056
+ type: 'done';
9057
+ } | {
9058
+ type: 'warn';
9059
+ message: string;
9060
+ } | {
9061
+ type: 'limit';
9062
+ message: string;
9063
+ resetAt?: string;
9064
+ } | {
9065
+ type: 'error';
9066
+ message: string;
9067
+ };
9068
+
9069
+ /**
9070
+ * In-process bus that streams ONE chat turn's progress to the GUI over the
9071
+ * multiplexed `/api/stream` WebSocket — the async replacement for the held-open
9072
+ * `POST /api/chat` SSE response. Modeled on {@link RenderProgressBus} / `FeedBus`,
9073
+ * but deliberately with NO `latest()` / replay buffer: a reconnecting client recovers
9074
+ * from the server-authoritative persisted `chat_messages` (checkpointed as the turn
9075
+ * streams), so replaying stale ticks to a fresh socket would only cause double-render.
9076
+ *
9077
+ * Every envelope carries the turn's `ownerUserId` so the `/api/stream` forwarder can
9078
+ * gate delivery PER USER — on a cloud workspace a chat is private to its author, and
9079
+ * this bus is per-process (shared across every connected socket), so the gate is
9080
+ * load-bearing, not decorative (see `chat-identity` / Stage 5).
9081
+ */
9082
+ interface ChatProgressEnvelope {
9083
+ /** The thread the turn belongs to. */
9084
+ threadId: string;
9085
+ /** The pending assistant message id — the client filters live events by (thread, message). */
9086
+ messageId: string;
9087
+ /** Cloud user id of the turn's owner; null on a local (single-user) workspace. */
9088
+ ownerUserId: string | null;
9089
+ /** The streamed chat event (same union the old SSE response wrote). */
9090
+ event: ChatStreamEvent;
9091
+ }
9092
+
7529
9093
  interface StartGuiServerOptions {
7530
9094
  /**
7531
9095
  * Active workspace config to open. NULL/empty ⇒ boot into the zero-workspace
@@ -7584,12 +9148,23 @@ interface StartGuiServerOptions {
7584
9148
  */
7585
9149
  realtimeWatchdogMs?: number;
7586
9150
  /**
7587
- * Run the in-process auto-update poll: while the GUI is open, check npm for a
9151
+ * Master switch for ALL auto-update behavior (default true). When false: the
9152
+ * in-process poll never runs (no registry/manifest fetch, no install, no
9153
+ * relaunch), `/api/update/status` reports `autoUpdate:false` / `action:'none'`,
9154
+ * and the desktop/CLI callers skip their own updaters too. Provided so the GUI
9155
+ * can be run pinned to its current version (testing, air-gapped, reproducible
9156
+ * demos). `GET /api/version` + `GET /api/update/status` still answer.
9157
+ */
9158
+ autoUpdate?: boolean;
9159
+ /**
9160
+ * Run the in-process auto-update poll: while the GUI is open, check for a
7588
9161
  * newer version and, when one lands on an installable copy, install it and
7589
9162
  * exit with the supervisor's restart code so it relaunches on the new version.
7590
9163
  * Set ONLY for a supervised child (`LATTICE_GUI_SUPERVISED=1`) — exiting to
7591
- * apply an update is safe only when a supervisor is there to respawn it.
7592
- * `GET /api/version` + `GET /api/update/status` are served regardless.
9164
+ * apply an update is safe only when a supervisor is there to respawn it. This
9165
+ * is the npm install-and-relaunch SUB-behavior; it is forced off when
9166
+ * `autoUpdate` is false. `GET /api/version` + `GET /api/update/status` are
9167
+ * served regardless.
7593
9168
  */
7594
9169
  selfUpdate?: boolean;
7595
9170
  /**
@@ -7599,6 +9174,29 @@ interface StartGuiServerOptions {
7599
9174
  * it overrides `selfUpdate`'s default factory.
7600
9175
  */
7601
9176
  updateServiceFactory?: (emit: (type: string, data: unknown) => void) => UpdateService;
9177
+ /**
9178
+ * Override the "is a newer version available?" probe. The desktop shell passes
9179
+ * a function that reads its release manifest (latest.json) — the same source
9180
+ * its bundled updater applies from — so the GUI surfaces a "restart to update"
9181
+ * hint without coupling the desktop to the npm registry. Omitted ⇒ the default
9182
+ * npm-registry check (web/CLI).
9183
+ */
9184
+ updateCheck?: (force: boolean) => Promise<string | null>;
9185
+ /**
9186
+ * Override the detected install context for the update service. The desktop
9187
+ * shell passes `{ kind:'desktop', installable:false, … }` so the status route
9188
+ * reports the desktop surface (→ `action:'restart-to-update'`) rather than
9189
+ * "unknown / not installable".
9190
+ */
9191
+ updateContext?: InstallContext;
9192
+ /**
9193
+ * Desktop shell only: apply a pending update via the bundled binary updater
9194
+ * (download + relaunch). Wired to `POST /api/update/apply` when the surface is
9195
+ * the desktop app, so the "Restart to update" pill triggers the real updater
9196
+ * instead of the npm install path (which the desktop can't use). Omitted ⇒ the
9197
+ * apply route uses the npm path / reports "not available".
9198
+ */
9199
+ desktopApplyUpdate?: () => void;
7602
9200
  /**
7603
9201
  * Desktop shell only: open an external URL in the OS default browser. The
7604
9202
  * embedded desktop webview has no tabs, so `target="_blank"` links are routed
@@ -7622,6 +9220,12 @@ interface GuiServerHandle {
7622
9220
  * resolves immediately for a non-cloud / virgin workspace.
7623
9221
  */
7624
9222
  whenConverged: () => Promise<void>;
9223
+ /**
9224
+ * TEST-ONLY: publish a chat-progress envelope directly into the active workspace's bus so
9225
+ * the per-user `/api/stream` delivery gate can be exercised without a live model turn.
9226
+ * Never invoked in production.
9227
+ */
9228
+ publishChatProgressForTest: (env: ChatProgressEnvelope) => void;
7625
9229
  }
7626
9230
  declare function startGuiServer(options: StartGuiServerOptions): Promise<GuiServerHandle>;
7627
9231
 
@@ -7760,6 +9364,14 @@ interface ProposedSchema {
7760
9364
  entities: InferredEntity[];
7761
9365
  dimensions: InferredDimension[];
7762
9366
  linkages: InferredLinkage[];
9367
+ /**
9368
+ * Link candidates in the marginal confidence band — at or above the "drop as
9369
+ * noise" floor but below the creation threshold (see `InferOptions.
9370
+ * minLinkConfidence`). NOT materialized: the referencing column survives as a
9371
+ * plain scalar column, and the importer asks the user whether to connect it
9372
+ * instead of guessing. Same shape as {@link linkages}, confidence included.
9373
+ */
9374
+ marginalLinks: InferredLinkage[];
7763
9375
  /** Top-level keys not imported (derived rollups, meta, scalars, column dictionaries). */
7764
9376
  skipped: {
7765
9377
  key: string;
@@ -7801,6 +9413,16 @@ declare function inferFieldType(values: unknown[]): InferredType;
7801
9413
  interface InferOptions {
7802
9414
  /** Override the inferred entity → table name (sourceKey → name). */
7803
9415
  rename?: Record<string, string>;
9416
+ /**
9417
+ * Confidence bar for creating a linkage (0..1, clamped). At or above it a
9418
+ * link is inferred as usual; in `[minLinkConfidence / 2, minLinkConfidence)`
9419
+ * the candidate is returned on {@link ProposedSchema.marginalLinks} instead
9420
+ * of being created (the referencing column stays a plain scalar column so a
9421
+ * later confirmation can still connect it); below the floor it is dropped as
9422
+ * noise. Dimension links (confidence 1) are unaffected. Defaults to
9423
+ * {@link DEFAULT_LINK_CONFIDENCE}.
9424
+ */
9425
+ minLinkConfidence?: number;
7804
9426
  }
7805
9427
  declare function inferSchema(data: Record<string, unknown>, opts?: InferOptions): ProposedSchema;
7806
9428
 
@@ -8016,4 +9638,4 @@ declare function dedupeAndDetectViews(plan: ProposedSchema, data: Record<string,
8016
9638
  views: DetectedView[];
8017
9639
  };
8018
9640
 
8019
- export { ALL_PROVENANCE_FIELDS, type AddWorkspaceOptions, type AdoptNativeOptions, type AdoptResult, type AggregateFunction, type AggregateHaving, type AggregateOptions, type AggregateResult, type AggregateSpec, type ApplyWriteResult, type AsOfCandidate, type AsOfColumnCandidate, type AsOfInputs, type AudienceRowCtx, type AuditEvent, type AuthorizeResult, type AutoUpdateResult, type BacklinkSignal, type BelongsToRelation, type BelongsToSource, type BenchmarkOptions, type BenchmarkReport, type BenchmarkScale, type BlobMetadata, BoundedReadError, type BuiltinTemplateName, CLOUD_SETTING_SYSTEM_PROMPT, CLOUD_SETTING_WORKSPACE_LOGO, CLOUD_SETTING_WORKSPACE_LOGO_ETAG, CONFIG_SUBDIR, CONNECTED_COLUMNS, CONNECTORS_TABLE, type CatalogEntity, type CatalogRecord, type ChangeEntry, type ChangelogOptions, type ChunkedMigrationOptions, type ChunkedMigrationResult, type ChunkerFn, type ClassifyMatch, type CleanupOptions, type CleanupResult, type CloudProbeResult, type CloudS3Secret, ComputedColumnCycleError, type ComputedColumnSpec, type ConnectedEdgeSpec, type ConnectedModelDef, ConnectedSourceImmutableError, type ConnectedVisibility, type ConnectionResult, type Connector, type ConnectorRecord, type ConnectorSource, type ConnectorStatus, ConnectorUnavailableError, type CountOptions, type CrawlOptions, type CrawlResult, type CreateConnectorInput, type CredentialConnector, type CredentialField, type CustomSignal, type CustomSource, DEFAULT_ENTRY_TYPES, DEFAULT_MAX_NODES, DEFAULT_STALE_MS, DEFAULT_TYPE_ALIASES, DenoSqliteAdapter, type DetectedView, type DiagnoseOptions, type DisconnectOptions, type DisconnectResult, type DiscoveredTable, EMBEDDINGS_TABLE, EmbeddingDimensionMismatchError, type EmbeddingRefreshResult, EmbeddingScanTooLargeError, type EmbeddingsConfig, type EnrichOptions, type EnrichResult, type EnrichedSource, type EnrichmentLookup, type EntityContextDefinition, type EntityContextManifestEntry, type EntityFileManifestInfo, type EntityFileSource, type EntityFileSpec, type EntityMatch, type EntityProfileField, type EntityProfileSection, type EntityProfileTemplate, type EntityRenderSpec, type EntityRenderTemplate, type EntitySectionPerRow, type EntitySectionsTemplate, type EntityTableColumn, type EntityTableTemplate, type EvalQuery, type EvalRegression, type ExistingTable, type ExtensionAvailability, type ExternalRecord, type ExtractEdgesSpec, type ExtractedObject, FileSourceKeyStore, type FileSourceKeyStoreOptions, type FilesRow, type Filter, type FilterAnd, type FilterExpr, type FilterOp, type FilterOr, FoldCache, type FtsConfig, type FtsGroup, type FtsHit, type FtsOptions, type FtsResult, type GraphBoostOptions, type GraphBoostResult, type GraphEdge, type GraphNode, type GraphTraversalResult, type GuiServerHandle, type HasManyRelation, type HasManySource, type HealthIssueKind, type HealthSeverity, type HybridScoreBreakdown, type HybridSearchOptions, type HybridSearchResult, IMMUTABLE_CONNECTED_FIELDS, type ImportMode, type ImportProgress, InMemorySourceKeyStore, InMemoryStateStore, type InferredColumn, type InferredDimension, type InferredEntity, type InferredLinkage, type InferredType, type InitOptions, JIRA_MODELS, type JiraClient, JiraConnector, type JiraCreds, LEGACY_MEMBER_GROUP, LOCAL_DB_RELPATH, type LatencyStats, Lattice, type LatticeConfig, type LatticeConfigInput, type LatticeEntityDef, type LatticeEntityRenderSpec, type LatticeFieldDef, type LatticeFieldType, type LatticeManifest, type LatticeOptions, type LinkOptions, type ListChangesContext, type LlmClient, type LlmMessage, MAX_TRAVERSAL_DEPTH, type ManyToManySource, type MarkdownTableColumn, type MaterializeCtx, type MaterializeOptions, type MaterializeResult, type MaterializedRollupSpec, type MigrateResult, type Migration, type MigrationCheckpoint, type MigrationOptions, type MigrationProgress, type MigrationResult, type MigrationStatus, type MultiTableDefinition, NATIVE_ENTITY_DEFS, NATIVE_ENTITY_NAMES, NATIVE_REGISTRY_TABLE, type Observation, type OrderBySpec, type OrganizeOptions, type OrganizeResult, type OrganizedCreation, type OrganizedLink, type ParseError, type ParseResult, type ParsedConfig, type PdfOptions, type PdfSenderInput, type PerQueryEval, type PkLookup, PostgresAdapter, type PostgresAdapterOptions, type PreparedStatement, type PrimaryKey, ProgressThrottle, type ProposedSchema, type ProvenanceConfig, type ProvenanceField, ProvenanceImmutableError, type QueryOptions, type QueryPageOptions, type QueryPageResult, type QueryProjection, READ_ONLY_HEADER, ROOT_DIRNAME, type RankingOptions, type ReadOnlyHeaderOptions, type RecencySignal, type ReconcileOptions, type ReconcileResult, type RefKind, type RefProvider, type ReferenceMetadata, ReferenceUnavailableError, type RefreshEmbeddingsOptions, type Relation, type RelevanceLabel, type RemoteBlobStore, type RenderHooks, type RenderOptions, type RenderProgress, type RenderProgressCallback, type RenderProgressKind, type RenderResult, type RenderSpec, type ReportConfig, type ReportResult, type ReportSection, type ReportSectionResult, type RerankCandidate, type RerankScore, type RerankerFn, type ResolveOptions, type RetrievalEvalOptions, type RetrievalEvalSummary, type RetrievalHealthIssue, type RetrievalHealthReport, type RetrievalHealthSpec, type RetrievalSlo, type Retriever, type RetryOptions, type ReverseSeedDetection, type ReverseSeedResult, type ReverseSeedTableResult, type ReverseSyncError, type ReverseSyncResult, type ReverseSyncUpdate, type RewardScores, type RewardSignal, type RollupFunction, type Row, type RowVisibilityDefault, type S3Config, type S3StoreConfig, S3UnavailableError, S3_SECRET_TABLE, SQLiteAdapter, type SchemaEntity, type SchemaMatch, type SearchOptions, type SearchResult, type SecurityOptions, type SeedConfig, type SeedLinkSpec, SeedReconciliationError, type SeedResult, type SelfSource, type SemanticChunkerOptions, type SessionEntry, type SessionParseOptions, type SessionWriteEntry, type SessionWriteOp, type SessionWriteParseResult, type SloViolation, type SourceHandle, type SourceKeyStore, type SourceMetadata, type SourceQueryOptions, SourceShreddedError, type StartGuiServerOptions, type StopFn, type StorageAdapter, type SyncConnectorOptions, type SyncConnectorResult, type SyncResult, TRELLO_MODELS, TRUST_COLUMNS, type TableDefinition, type TableHealth, type TablePolicy, type TemplateRenderSpec, type TextChunk, type ToolkitPresentation, type TraversalDirection, type TraversalNode, type TraversalOptions, type TrelloClient, TrelloConnector, type TrelloCreds, type TrelloPageOpts, type TrustConfig, type TrustState, type TurnParams, type TurnResult, type UnresolvedLink, type UpsertByNaturalKeyOptions, type UserIdentity, type UserPreferences, type VectorHit, type Viewer, type VisionOptions, type VisionSenderInput, WORKSPACES_SUBDIR, type WatchOptions, type WorkspacePaths, type WorkspaceRecord, type WorkspaceRegistry, type WriteHook, type WriteHookContext, type WritebackDefinition, type WritebackStateStore, type WritebackValidationResult, activeWorkspaceLabel, addEdge, addEdges, addWorkspace, adoptNativeEntities, allComputedDeps, analyticsEnabled, applyChunkedMigration, applyReranker, applyTokenBudget, applyWriteEntry, archiveLocalSqlite, assertSafeUrl, attachBlob, audiencePredicate, audienceViewSql, autoFtsColumns, autoUpdate, backfillOwnership, backlinkBoost, benchmarkRetrieval, buildVectorIndex, builtinConnectors, canManageRoles, checkSlos, chunkText, classifyLinks, clearJiraCreds, clearTrelloCreds, cloudRlsInstalled, collectConnectorKeys, computeColumns, computedColumnDdl, computedColumnOrder, concatRowText, configDir, connectedColumns, contentHash, cosineSimilarity, crawlUrl, createConnector, createReadOnlyHeader, createS3Store, createSQLiteStateStore, decrypt, dedupeAndDetectViews, defaultWorkspaceYaml, defineJiraTables, defineTrelloTables, deleteConnectorRecord, deleteDbCredential, deleteToken, deriveCanonicalContexts, deriveKey, describeImage, describePdf, detectAsOf, detectAsOfCandidates, detectAsOfColumns, detectRetrievalRegressions, diagnoseRetrieval, disconnectConnector, discoverCloudTables, dropVectorIndex, enableAudienceView, enableChangelogRls, enableConnectorRls, enableRlsForTable, encrypt, enrichKnowledge, ensureCheckpointTable, ensureConnectorRegistry, ensureEdgesTable, ensureEmbeddingsTable, ensureFtsIndex, ensureLatticeRoot, entityFileNames, estimateTokens, evaluateRetrieval, excelToRecords, extractEdgesFromColumn, extractObjects, filePresignSql, findLatticeRoot, fixSchemaConflicts, foldEntity, formatHealthReport, frontmatter, ftsTableName, fullTextSearch, generateEntryId, generateMemberPassword, generateWriteEntryId, getActiveWorkspace, getCloudSetting, getConnector, getConnectorByToolkit, getDbCredential, getJiraCreds, getMigrationCheckpoint, getOrCreateMasterKey, getTablePolicy, getTrelloCreds, getWorkspace, grantPresignerToMemberGroup, graphAdjacencyBoost, hasFilePresigner, hasFtsIndex, hasVectorIndex, hashFile, hybridSearch, importLegacyUserConfig, inferFieldType, inferSchema, installCloudRls, installCloudSettings, installFilePresigner, isCredentialConnector, isEncrypted, isNativeEntity, isPostgresUrl, isPrivateIp, isRetryableDbError, isRowAudience, latencyStats, listConnectors, listDbCredentials, listMigrationCheckpoints, listNativeBindings, listTokens, listWorkspaces, loadColumnPolicy, loadJiraClient, loadTrelloClient, manifestPath, markdownTable, matchSchemaToExisting, materializeImport, memberGroupFor, memberRoleName, migrateLatticeData, neighbors, normalizeName, observationVisible, observationsFromChange, openTargetLatticeForMigration, openUnderSource, organizeSource, parseCellDate, parseConfigFile, parseConfigString, parseMarkdownEntries, parseMatches, parseObjects, parseSessionMD, parseSessionWrites, percentile, probeCloud, provenanceColumns, providerForUrl, provisionMemberRole, rankingBoost, readIdentity, readManifest, readPreferences, readRegistry, readToken, recencyBoost, recordSync, referenceLocalFile, referenceUrl, refreshEmbeddings, regenerateAudienceViewFromDb, registerNativeEntities, registryPath, removeEdge, removeEmbedding, renameEntities, resolveActiveS3Config, resolveConnectorIdentity, resolveLatticeRoot, resolveProvenanceFields, resolveSource, resolveTrustDefault, resolveWorkspacePaths, resumeMigration, revertMigration, revokeMemberRole, rewardBoost, rollupColumnDdl, rootConfigDir, s3Key, saveDbCredential, saveDbCredentialForTeam, sealUnderSource, searchByEmbedding, searchVectorIndex, secureCloud, secureConnectorTables, seedColumnPolicyFromYaml, semanticChunker, setActiveWorkspace, setCloudS3Secret, setCloudSetting, setColumnAudience, setConnectorStatus, setJiraCreds, setRowVisibility, setTableDefaultVisibility, setTableNeverShare, setTrelloCreds, shredSource, slugify, sourceRecords, startGuiServer, storeEmbedding, summarizeText, syncConnector, syncIfStale, syncStaleConnectors, tableNeedsAudienceView, toSafeDirName, traverse, truncate, updateConnectorConnection, validateEntryId, vectorIndexAvailable, vectorIndexName, withRetry, workspaceBlobsDir, workspaceConfigPath, workspaceContextDir, workspaceDataDir, workspaceDbPath, workspaceDir, workspacesDir, writeIdentity, writeManifest, writePreferences, writeRegistry, writeToken };
9641
+ export { AI_CELL_TABLE, AI_MAP_TABLE, ALL_PROVENANCE_FIELDS, type AddWorkspaceOptions, type AdoptNativeOptions, type AdoptResult, type AggregateFunction, type AggregateHaving, type AggregateOptions, type AggregateResult, type AggregateSpec, type ApplyWriteResult, type AsOfCandidate, type AsOfColumnCandidate, type AsOfInputs, type AudienceRowCtx, type AuditEvent, type AuthorizeResult, type AutoUpdateResult, type BacklinkSignal, type BelongsToRelation, type BelongsToSource, type BenchmarkOptions, type BenchmarkReport, type BenchmarkScale, type BlobMetadata, type BoundedCountOptions, BoundedReadError, type BuiltinTemplateName, CLOUD_SETTING_SYSTEM_PROMPT, CLOUD_SETTING_WORKSPACE_LOGO, CLOUD_SETTING_WORKSPACE_LOGO_ETAG, COMPUTED_STATE_TABLE, CONFIG_SUBDIR, CONNECTED_COLUMNS, CONNECTORS_TABLE, type CalcEmitContext, type CalcExpr, CalcExprError, type CalcNode, type CalcRefResolver, type CatalogEntity, type CatalogRecord, type ChangeEntry, type ChangelogOptions, type ChunkedMigrationOptions, type ChunkedMigrationResult, type ChunkerFn, type ClassifyMatch, type CleanupOptions, type CleanupResult, type CloudProbeResult, type CloudS3Secret, type CompiledAiField, type CompiledComputedTable, ComputedColumnCycleError, type ComputedColumnSpec, type ComputedFieldDef, type ComputedFieldState, type ComputedFillOptions, type ComputedFillReport, type ComputedRegistrationResult, type ComputedSchema, type ComputedSchemaTable, ComputedTableCycleError, type ComputedTableDef, type ComputedTableHost, type ConnectedEdgeSpec, type ConnectedModelDef, ConnectedSourceImmutableError, type ConnectedVisibility, type ConnectionResult, type Connector, type ConnectorRecord, type ConnectorSource, type ConnectorStatus, ConnectorUnavailableError, type CountOptions, type CrawlOptions, type CrawlResult, type CreateConnectorInput, type CredentialConnector, type CredentialField, type CustomSignal, type CustomSource, DEFAULT_ENTRY_TYPES, DEFAULT_MAX_NODES, DEFAULT_STALE_MS, DEFAULT_TYPE_ALIASES, DatabaseConnector, type DbColumnDesc, type DbSchemaDescriptor, type DbTableDesc, DenoSqliteAdapter, type DetectedView, type DiagnoseOptions, type DisconnectOptions, type DisconnectResult, type DiscoveredTable, EMBEDDINGS_TABLE, EmbeddingDimensionMismatchError, type EmbeddingRefreshResult, EmbeddingScanTooLargeError, type EmbeddingsConfig, type EnrichOptions, type EnrichResult, type EnrichedSource, type EnrichmentLookup, type EntityContextDefinition, type EntityContextManifestEntry, type EntityFileManifestInfo, type EntityFileSource, type EntityFileSpec, type EntityMatch, type EntityProfileField, type EntityProfileSection, type EntityProfileTemplate, type EntityRenderSpec, type EntityRenderTemplate, type EntitySectionPerRow, type EntitySectionsTemplate, type EntityTableColumn, type EntityTableTemplate, type EvalQuery, type EvalRegression, type ExistingTable, type ExtensionAvailability, type ExternalRecord, type ExtractEdgesSpec, type ExtractedObject, type FieldFillResult, FileSourceKeyStore, type FileSourceKeyStoreOptions, type FilesRow, type FillLlm, type Filter, type FilterAnd, type FilterExpr, type FilterOp, type FilterOr, FoldCache, type FtsConfig, type FtsGroup, type FtsHit, type FtsOptions, type FtsResult, type GraphBoostOptions, type GraphBoostResult, type GraphEdge, type GraphNode, type GraphTraversalResult, type GuiServerHandle, type HasManyRelation, type HasManySource, type HealthIssueKind, type HealthSeverity, type HybridScoreBreakdown, type HybridSearchOptions, type HybridSearchResult, IMMUTABLE_CONNECTED_FIELDS, type ImportMode, type ImportProgress, InMemorySourceKeyStore, InMemoryStateStore, type InferredColumn, type InferredDimension, type InferredEntity, type InferredLinkage, type InferredType, type InitOptions, type IntrospectiveSpec, LEGACY_MEMBER_GROUP, LOCAL_DB_RELPATH, type LatencyStats, Lattice, type LatticeConfig, type LatticeConfigInput, type LatticeEntityDef, type LatticeEntityRenderSpec, type LatticeFieldDef, type LatticeFieldType, type LatticeManifest, type LatticeOptions, type LinkOptions, type ListChangesContext, type LlmClient, type LlmMessage, MAX_TRAVERSAL_DEPTH, type ManyToManySource, type MarkdownTableColumn, type MaterializeCtx, type MaterializeOptions, type MaterializeResult, type MaterializedRollupSpec, type McpBeginResult, type McpConnector, McpConnectorBase, type McpConnectorDeps, type McpConnectorSpec, type McpModelBinding, type McpOAuthDriver, type McpServerRef, type McpServerSpec, type McpToolCall, type McpToolInfo, type McpTransport, type McpTransportFactory, type MigrateResult, type Migration, type MigrationCheckpoint, type MigrationOptions, type MigrationProgress, type MigrationResult, type MigrationStatus, type MultiTableDefinition, NATIVE_ENTITY_DEFS, NATIVE_ENTITY_NAMES, NATIVE_REGISTRY_TABLE, type Observation, type OrderBySpec, type OrganizeOptions, type OrganizeResult, type OrganizedCreation, type OrganizedLink, type PageOpts, type ParseError, type ParseResult, type ParsedConfig, type PdfOptions, type PdfSenderInput, type PerQueryEval, type PkLookup, PostgresAdapter, type PostgresAdapterOptions, PostgresDialect, type PreparedStatement, type PrimaryKey, ProgressThrottle, type ProposedSchema, type ProvenanceConfig, type ProvenanceField, ProvenanceImmutableError, type QueryOptions, type QueryPageOptions, type QueryPageResult, type QueryProjection, READ_ONLY_HEADER, ROOT_DIRNAME, type RankingOptions, type ReadOnlyHeaderOptions, type RecencySignal, type ReconcileOptions, type ReconcileResult, type RefKind, type RefProvider, type ReferenceMetadata, ReferenceUnavailableError, type RefreshEmbeddingsOptions, type RegisterComputedTablesOptions, type Relation, type RelevanceLabel, type RemoteBlobStore, type RenderHooks, type RenderOptions, type RenderProgress, type RenderProgressCallback, type RenderProgressKind, type RenderResult, type RenderSpec, type ReportConfig, type ReportResult, type ReportSection, type ReportSectionResult, type RerankCandidate, type RerankScore, type RerankerFn, type ResolveOptions, type RetrievalEvalOptions, type RetrievalEvalSummary, type RetrievalHealthIssue, type RetrievalHealthReport, type RetrievalHealthSpec, type RetrievalSlo, type Retriever, type RetryOptions, type ReverseSeedDetection, type ReverseSeedResult, type ReverseSeedTableResult, type ReverseSyncError, type ReverseSyncResult, type ReverseSyncUpdate, type RewardScores, type RewardSignal, type RollupFunction, type Row, type RowVisibilityDefault, type S3Config, type S3StoreConfig, S3UnavailableError, S3_SECRET_TABLE, SQLiteAdapter, type SchemaEntity, type SchemaMatch, type SearchOptions, type SearchResult, type SecurityOptions, type SeedConfig, type SeedLinkSpec, SeedReconciliationError, type SeedResult, type SelfSource, type SemanticChunkerOptions, type SessionEntry, type SessionParseOptions, type SessionWriteEntry, type SessionWriteOp, type SessionWriteParseResult, SimpleMcpConnector, type SloViolation, type SourceHandle, type SourceKeyStore, type SourceMetadata, type SourceQueryOptions, SourceShreddedError, type SqlDialect, type SqlQuery, type StartGuiServerOptions, type StopFn, type StorageAdapter, type SyncConnectorOptions, type SyncConnectorResult, type SyncResult, TRUST_COLUMNS, type TableDefinition, type TableHealth, type TablePolicy, type TemplateRenderSpec, type TextChunk, type ToolkitPresentation, type TraversalDirection, type TraversalNode, type TraversalOptions, type TrustConfig, type TrustState, type TurnParams, type TurnResult, type UnresolvedLink, type UpsertByNaturalKeyOptions, type UserIdentity, type UserPreferences, type VectorHit, type VectorIndexOptions, type Viewer, type VisionOptions, type VisionSenderInput, WORKSPACES_SUBDIR, type WatchOptions, type WorkspacePaths, type WorkspaceRecord, type WorkspaceRegistry, type WriteHook, type WriteHookContext, type WritebackDefinition, type WritebackStateStore, type WritebackValidationResult, activeWorkspaceLabel, addEdge, addEdges, addWorkspace, adoptNativeEntities, allComputedDeps, analyticsEnabled, applyChunkedMigration, applyReranker, applyTokenBudget, applyWriteEntry, archiveLocalSqlite, assembleConnectionString, assertSafeUrl, attachBlob, audiencePredicate, audienceViewSql, autoFtsColumns, autoUpdate, backfillOwnership, backlinkBoost, benchmarkRetrieval, buildModelDefs, buildVectorIndex, builtinConnectors, canManageRoles, checkSlos, chunkText, classifyLinks, clearDbSourceCreds, clearSchemaDescriptor, cloudRlsInstalled, collectConnectorKeys, compileComputedTable, computeColumns, computedColumnDdl, computedColumnOrder, computedTableOrder, concatRowText, configDir, connectDirect, connectedColumns, contentHash, cosineSimilarity, crawlUrl, createConnector, createReadOnlyHeader, createS3Store, createSQLiteStateStore, decrypt, dedupeAndDetectViews, defaultWorkspaceYaml, deleteConnectorRecord, deleteDbCredential, deleteToken, deriveCanonicalContexts, deriveKey, describeConnectedSources, describeImage, describePdf, detectAsOf, detectAsOfCandidates, detectAsOfColumns, detectRetrievalRegressions, diagnoseRetrieval, dialectFor, disconnectConnector, discoverCloudTables, dropVectorIndex, emitCalcExpr, enableAudienceView, enableChangelogRls, enableConnectorRls, enableRlsForTable, encrypt, enrichKnowledge, ensureAiTables, ensureCheckpointTable, ensureConnectorRegistry, ensureEdgesTable, ensureEmbeddingsTable, ensureFtsIndex, ensureLatticeRoot, entityFileNames, estimateTokens, evaluateRetrieval, excelToRecords, extractEdgesFromColumn, extractObjects, filePresignSql, findLatticeRoot, fixSchemaConflicts, foldEntity, formatHealthReport, frontmatter, ftsTableName, fullTextSearch, generateEntryId, generateMemberPassword, generateWriteEntryId, genericConnector, getActiveWorkspace, getCloudSetting, getConnector, getConnectorByToolkit, getDbCredential, getDbSourceCreds, getMigrationCheckpoint, getOrCreateMasterKey, getSchemaDescriptor, getTablePolicy, getWorkspace, grantMemberAccess, grantPresignerToMemberGroup, graphAdjacencyBoost, hasFilePresigner, hasFtsIndex, hasVectorIndex, hashFile, hybridSearch, importLegacyUserConfig, inferFieldType, inferSchema, installCloudRls, installCloudSettings, installFilePresigner, introspectiveConnector, isCredentialConnector, isEncrypted, isMcpConnector, isNativeEntity, isPostgresUrl, isPrivateIp, isRetryableDbError, isRowAudience, latencyStats, latticeTableName, listConnectors, listDbCredentials, listMigrationCheckpoints, listNativeBindings, listTokens, listWorkspaces, loadColumnPolicy, manifestPath, markdownTable, matchSchemaToExisting, materializeImport, mcpModel, memberGroupFor, memberRoleName, migrateLatticeData, neighbors, normalizeName, observationVisible, observationsFromChange, openTargetLatticeForMigration, openUnderSource, organizeSource, parseCalcExpr, parseCellDate, parseConfigFile, parseConfigString, parseMarkdownEntries, parseMatches, parseObjects, parseSessionMD, parseSessionWrites, percentile, probeCloud, provenanceColumns, providerForUrl, provisionMemberRole, purgeAiField, rankingBoost, readComputedState, readIdentity, readManifest, readPreferences, readRegistry, readToken, recencyBoost, recordSync, referenceLocalFile, referenceUrl, refreshEmbeddings, regenerateAudienceViewFromDb, registerComputedTables, registerNativeEntities, registryPath, removeEdge, removeEmbedding, renameEntities, resolveActiveS3Config, resolveConnectorIdentity, resolveLatticeRoot, resolveProvenanceFields, resolveSource, resolveTrustDefault, resolveWorkspacePaths, resumeMigration, revertMigration, revokeMemberRole, rewardBoost, rollupColumnDdl, rootConfigDir, runComputedFill, s3Key, saveDbCredential, saveDbCredentialForTeam, sealUnderSource, searchByEmbedding, searchVectorIndex, secureCloud, secureConnectorTables, seedColumnPolicyFromYaml, semanticChunker, setActiveWorkspace, setCloudS3Secret, setCloudSetting, setColumnAudience, setConnectorStatus, setDbSourceCreds, setRowVisibility, setSchemaDescriptor, setTableDefaultVisibility, setTableNeverShare, shredSource, slugify$1 as slugify, slugify as slugifyDbName, sourceRecords, startGuiServer, storeEmbedding, summarizeText, syncConnector, syncIfStale, syncStaleConnectors, tableNeedsAudienceView, toSafeDirName, traverse, truncate, updateConnectorConnection, validateEntryId, vectorIndexAvailable, vectorIndexName, withRetry, workspaceBlobsDir, workspaceConfigPath, workspaceContextDir, workspaceDataDir, workspaceDbPath, workspaceDir, workspacesDir, writeIdentity, writeManifest, writePreferences, writeRegistry, writeToken };