@trilogy-data/trilogy-studio-components 0.1.24 → 0.1.26

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.
Files changed (116) hide show
  1. package/README.md +5 -0
  2. package/dist/AssetAutoImporter-CBzvBaOt.js +405 -0
  3. package/dist/ChatCreatorModal-CL-jBnc3.js +218 -0
  4. package/dist/ChatModelSelect-Do-VRUFc.js +40 -0
  5. package/dist/{CommunityModels-CsDDXyIL.js → CommunityModels-Cmro0hV3.js} +101 -70
  6. package/dist/{Dashboard-BJfgRfRy.js → Dashboard-BH9ZwMVJ.js} +8 -8
  7. package/dist/{DashboardCreatorInline-wAlx6oH7.js → DashboardCreatorInline-CDU7kLy3.js} +3 -3
  8. package/dist/{DashboardImportSelector-D6ufVZIk.js → DashboardImportSelector-DQSvu9T-.js} +1 -1
  9. package/dist/{DashboardMobile-DoOyBHN7.js → DashboardMobile-BUPtdN2V.js} +4 -4
  10. package/dist/{Editor-Bs-h7edF.js → Editor-DQsYpkwl.js} +6 -6
  11. package/dist/{GlobalChatPanel-YsDZGxg8.js → GlobalChatPanel-DhnOaige.js} +155 -119
  12. package/dist/{IDE-CLF-KH2r.js → IDE-HLa8vybk.js} +9 -9
  13. package/dist/{JobsView-D1pp2xi5.js → JobsView-B97HBoIE.js} +8 -8
  14. package/dist/{LLMChat-CwM_brSg.js → LLMChat-CxJcPLEG.js} +1 -1
  15. package/dist/{LLMView-BiwCZb3Q.js → LLMView-C1MUf0c2.js} +21 -21
  16. package/dist/{MobileIDE-DrWh_OlB.js → MobileIDE-BpqXoaMR.js} +258 -253
  17. package/dist/{ModelCard-CsW4UwZI.js → ModelCard-BhN25jaV.js} +10 -10
  18. package/dist/{ModelCreator-C2douDis.js → ModelCreator-CCdC7EXt.js} +1 -1
  19. package/dist/{ModelView-D7ENFV2U.js → ModelView-DUDA_PtZ.js} +3 -3
  20. package/dist/{PageLoading-C5WK5i5Y.js → PageLoading-BzniJM0C.js} +622 -619
  21. package/dist/{ResultComponent-1MQlbELb.js → ResultComponent-CAn_r3BL.js} +3 -3
  22. package/dist/{Results-DXIYS4iW.js → Results-BJtlwebY.js} +3 -3
  23. package/dist/{SymbolsPane-BM6dUN-h.js → SymbolsPane-KYSwN9Sv.js} +1 -1
  24. package/dist/{TutorialPage-HXbXCznh.js → TutorialPage-NRcee6Dx.js} +8 -8
  25. package/dist/{VegaLiteChart-CS4h_t-K.js → VegaLiteChart-D1slfOUY.js} +11227 -11195
  26. package/dist/{WelcomePage-CLKvAvk-.js → WelcomePage-D5mU2x1L.js} +1 -1
  27. package/dist/components/AssetAutoImporter.vue.d.ts.map +1 -1
  28. package/dist/components/TipsCTA.vue.d.ts +1 -1
  29. package/dist/components/VegaLiteChart.vue.d.ts.map +1 -1
  30. package/dist/components/chartHelpers.d.ts +9 -1
  31. package/dist/components/chartHelpers.d.ts.map +1 -1
  32. package/dist/components/chartRenderManager.d.ts +4 -1
  33. package/dist/components/chartRenderManager.d.ts.map +1 -1
  34. package/dist/components/community/AddStoreModal.vue.d.ts +1 -1
  35. package/dist/components/community/AddStoreModal.vue.d.ts.map +1 -1
  36. package/dist/components/community/CommunityModelCard.vue.d.ts.map +1 -1
  37. package/dist/components/editor/Editor.vue.d.ts.map +1 -1
  38. package/dist/components/jobs/JobsAddStoreModal.vue.d.ts.map +1 -1
  39. package/dist/components/llm/ChatCreatorModal.vue.d.ts +17 -1
  40. package/dist/components/llm/ChatCreatorModal.vue.d.ts.map +1 -1
  41. package/dist/components/llm/ChatModelSelect.vue.d.ts +13 -0
  42. package/dist/components/llm/ChatModelSelect.vue.d.ts.map +1 -0
  43. package/dist/components/llm/GlobalChatPanel.vue.d.ts.map +1 -1
  44. package/dist/components/sidebar/CommunityModelList.vue.d.ts +33 -50
  45. package/dist/components/sidebar/CommunityModelList.vue.d.ts.map +1 -1
  46. package/dist/components/sidebar/CommunityModelListItem.vue.d.ts.map +1 -1
  47. package/dist/components/sidebar/EditorList.vue.d.ts +32 -49
  48. package/dist/components/sidebar/EditorList.vue.d.ts.map +1 -1
  49. package/dist/components/sidebar/Sidebar.vue.d.ts +65 -99
  50. package/dist/components/sidebar/Sidebar.vue.d.ts.map +1 -1
  51. package/dist/dashboard.js +52 -52
  52. package/dist/{dashboardStore-BzmI4OCV.js → dashboardStore-ppM6nGks.js} +33 -33
  53. package/dist/dashboards/layerSpec.d.ts +38 -4
  54. package/dist/dashboards/layerSpec.d.ts.map +1 -1
  55. package/dist/dashboards/lineAreaSpec.d.ts +8 -80
  56. package/dist/dashboards/lineAreaSpec.d.ts.map +1 -1
  57. package/dist/dashboards/mapSpec.d.ts +1 -0
  58. package/dist/dashboards/mapSpec.d.ts.map +1 -1
  59. package/dist/dashboards/spec.d.ts.map +1 -1
  60. package/dist/data/tutorial/documentation.d.ts.map +1 -1
  61. package/dist/data/tutorial/startupScripts.d.ts +3 -0
  62. package/dist/data/tutorial/startupScripts.d.ts.map +1 -0
  63. package/dist/{demoSetup-vmiMeH4_.js → demoSetup-mwa-RDC6.js} +19 -18
  64. package/dist/{editorRefinementRuntime-hC3u8GMO.js → editorRefinementRuntime-DoP9P5J2.js} +892 -871
  65. package/dist/{editorRefinementToolExecutor-Bomrpmns.js → editorRefinementToolExecutor-CVY3FA7k.js} +392 -267
  66. package/dist/{editorStore-Bj3Qnatp.js → editorStore-BhhmXbi-.js} +230 -183
  67. package/dist/entry.dashboard.d.ts +1 -0
  68. package/dist/entry.dashboard.d.ts.map +1 -1
  69. package/dist/{globalChatRuntime-DXI2KyGn.js → globalChatRuntime-DWZxeS57.js} +13 -13
  70. package/dist/{helpers-CFp2QCvv.js → helpers-BLQAgX3C.js} +17 -16
  71. package/dist/llm/chatAgentPrompt.d.ts.map +1 -1
  72. package/dist/llm/data/constants.d.ts +1 -1
  73. package/dist/llm/data/constants.d.ts.map +1 -1
  74. package/dist/llm/data/trilogySyntax.generated.d.ts +7 -1
  75. package/dist/llm/data/trilogySyntax.generated.d.ts.map +1 -1
  76. package/dist/llm/docsIndex.d.ts.map +1 -1
  77. package/dist/llm/openai.d.ts +2 -1
  78. package/dist/llm/openai.d.ts.map +1 -1
  79. package/dist/{llm-BuEsqH9Q.js → llm-BBOVWb-F.js} +7 -5
  80. package/dist/llm.js +3 -3
  81. package/dist/{llmStore-x5lRrKgx.js → llmStore-DJM2JhzY.js} +2 -2
  82. package/dist/{localStorage-C-ZtI6YA.js → localStorage-D9uyOFEO.js} +14 -14
  83. package/dist/models/helpers.d.ts.map +1 -1
  84. package/dist/monaco.js +1 -1
  85. package/dist/{reconcile-E5LR5p_f.js → reconcile-Dyf4UZmj.js} +1 -1
  86. package/dist/{remoteStoreSync-MwuJpHHt.js → remoteStoreSync-Z-yjlYtI.js} +1 -1
  87. package/dist/remotes/displayHelpers.d.ts.map +1 -1
  88. package/dist/remotes/genericStoreMetadata.d.ts +1 -0
  89. package/dist/remotes/genericStoreMetadata.d.ts.map +1 -1
  90. package/dist/remotes/models.d.ts +37 -5
  91. package/dist/remotes/models.d.ts.map +1 -1
  92. package/dist/remotes/staticStorePresets.d.ts +22 -0
  93. package/dist/remotes/staticStorePresets.d.ts.map +1 -0
  94. package/dist/remotes/storeService.d.ts +25 -4
  95. package/dist/remotes/storeService.d.ts.map +1 -1
  96. package/dist/stores/communityApiStore.d.ts +32 -60
  97. package/dist/stores/communityApiStore.d.ts.map +1 -1
  98. package/dist/stores/connectionStore.d.ts.map +1 -1
  99. package/dist/stores/resolver.d.ts +5 -0
  100. package/dist/stores/resolver.d.ts.map +1 -1
  101. package/dist/stores/urlStore.d.ts +1 -0
  102. package/dist/stores/urlStore.d.ts.map +1 -1
  103. package/dist/{stores-D-gHjj-_.js → stores-BT9hTLny.js} +47 -47
  104. package/dist/stores.js +7 -7
  105. package/dist/trilogy-studio-components.css +1 -1
  106. package/dist/{urlStore-Dgmwx1YI.js → urlStore-52HG6lsX.js} +1 -0
  107. package/dist/{useChatWithTools-BlCJrJT7.js → useChatWithTools-B0HZ_DVh.js} +1 -1
  108. package/dist/{useDashboard-CVlbV17V.js → useDashboard-CdSnPe_6.js} +186 -186
  109. package/dist/{useGlobalChatPanel-CNARTFYu.js → useGlobalChatPanel-D_0luajQ.js} +2 -2
  110. package/dist/views/MobileIDE.vue.d.ts.map +1 -1
  111. package/dist/views/TutorialPage.vue.d.ts +32 -49
  112. package/dist/views/TutorialPage.vue.d.ts.map +1 -1
  113. package/dist/views.js +16 -16
  114. package/package.json +1 -1
  115. package/dist/AssetAutoImporter-BD1P91Hq.js +0 -403
  116. package/dist/ChatCreatorModal-BTqY06B6.js +0 -177
@@ -1,13 +1,13 @@
1
1
  import { r as e } from "./results-DTgAKr5j.js";
2
- import { $ as t, $t as n, At as r, B as i, Et as a, Gt as o, H as s, I as c, Jt as l, K as u, Kt as d, L as f, O as p, Ot as m, R as h, Ut as g, Wt as ee, X as te, Xt as ne, Yt as re, Z as ie, Zt as ae, a as _, at as v, c as oe, ct as se, dt as ce, en as le, ft as ue, i as de, it as fe, jt as pe, kt as me, l as he, lt as ge, n as _e, o as ve, ot as ye, pt as be, qt as xe, r as y, rt as Se, s as Ce, t as we, ut as Te, y as b, yt as x } from "./editorRefinementToolExecutor-Bomrpmns.js";
3
- import { s as Ee } from "./base-C8IuZWqY.js";
4
- import { t as De } from "./connectionHistoryStorage-DKJHiKJY.js";
5
- import { defineStore as Oe } from "pinia";
2
+ import { $t as t, At as n, Gt as r, Jt as i, Kt as a, L as o, Mt as s, Nt as c, Ot as l, Q as u, Qt as d, R as f, U as p, V as m, Xt as h, Yt as g, Z as ee, Zt as te, a as _, at as ne, bt as re, c as ie, dt as ae, et as oe, ft as se, i as ce, it as le, jt as ue, k as v, l as de, lt as fe, mt as pe, n as me, nn as he, o as ge, ot as y, pt as _e, q as ve, qt as ye, r as b, s as be, st as xe, t as Se, tn as x, ut as Ce, xt as S, y as C, z as w } from "./editorRefinementToolExecutor-CVY3FA7k.js";
3
+ import { s as we } from "./base-C8IuZWqY.js";
4
+ import { t as Te } from "./connectionHistoryStorage-DKJHiKJY.js";
5
+ import { defineStore as Ee } from "pinia";
6
6
  //#region editors/fileTypes.ts
7
- var S = (e) => {
7
+ var T = (e) => {
8
8
  let t = e.lastIndexOf(".");
9
9
  return t === -1 ? "" : e.slice(t).toLowerCase();
10
- }, ke = (e) => {
10
+ }, De = (e) => {
11
11
  switch (e) {
12
12
  case "sql": return ".sql";
13
13
  case "python": return ".py";
@@ -15,11 +15,11 @@ var S = (e) => {
15
15
  case "csv": return ".csv";
16
16
  default: return ".preql";
17
17
  }
18
- }, C = (e, t) => {
18
+ }, E = (e, t) => {
19
19
  let n = e.trim();
20
- return !n || /\.(preql|sql|csv|py|md|markdown|txt)$/i.test(n) ? n : `${n}${ke(t)}`;
21
- }, w = (e) => {
22
- switch (S(e)) {
20
+ return !n || /\.(preql|sql|csv|py|md|markdown|txt)$/i.test(n) ? n : `${n}${De(t)}`;
21
+ }, D = (e) => {
22
+ switch (T(e)) {
23
23
  case ".preql": return "preql";
24
24
  case ".sql": return "sql";
25
25
  case ".py": return "python";
@@ -29,7 +29,7 @@ var S = (e) => {
29
29
  case ".csv": return "csv";
30
30
  default: return null;
31
31
  }
32
- }, Ae = (e) => {
32
+ }, Oe = (e) => {
33
33
  switch (e) {
34
34
  case "sql":
35
35
  case "csv": return "sql";
@@ -37,23 +37,23 @@ var S = (e) => {
37
37
  case "markdown": return "markdown";
38
38
  default: return "trilogy";
39
39
  }
40
- }, T = (e) => e === "trilogy" || e === "preql", je = (e) => T(e), Me = (e) => T(e), Ne = (e) => T(e), Pe = (e) => T(e) || e === "sql", Fe = (e) => e !== "python" && e !== "markdown" && e !== "csv", Ie = (e) => {
41
- let t = S(e);
40
+ }, O = (e) => e === "trilogy" || e === "preql", ke = (e) => O(e), Ae = (e) => O(e), je = (e) => O(e), Me = (e) => O(e) || e === "sql", Ne = (e) => e !== "python" && e !== "markdown" && e !== "csv", Pe = (e) => {
41
+ let t = T(e);
42
42
  return t !== ".py" && t !== ".md" && t !== ".csv";
43
- }, E = /* @__PURE__ */ function(e) {
43
+ }, k = /* @__PURE__ */ function(e) {
44
44
  return e.SOURCE = "source", e.STARTUP_SCRIPT = "startup_script", e;
45
45
  }({});
46
- function Le(e) {
47
- return e.storage === "remote" ? Ee({
46
+ function Fe(e) {
47
+ return e.storage === "remote" ? we({
48
48
  name: e.connection,
49
49
  storage: "remote",
50
50
  remoteStoreId: e.remoteStoreId || null
51
- }) : Ee({
51
+ }) : we({
52
52
  name: e.connection,
53
53
  storage: "local"
54
54
  });
55
55
  }
56
- var Re = class t {
56
+ var Ie = class t {
57
57
  id;
58
58
  name;
59
59
  type;
@@ -94,11 +94,11 @@ var Re = class t {
94
94
  }
95
95
  }
96
96
  constructor({ id: t, name: n, type: r, connection: i, storage: a, contents: o = null, tags: s = null, remoteStoreId: c = null, remotePath: l = null, remoteOriginalPath: u = null, remotePersisted: d = !1 }) {
97
- this.id = t, this.name = a === "remote" ? C(n, r) : n, this.type = w(this.name) ?? r, this.syntax = this.type === "python" ? "python" : "preql", this.connection = i, this.connectionId = Le({
97
+ this.id = t, this.name = a === "remote" ? E(n, r) : n, this.type = D(this.name) ?? r, this.syntax = this.type === "python" ? "python" : "preql", this.connection = i, this.connectionId = Fe({
98
98
  connection: i,
99
99
  storage: a,
100
100
  remoteStoreId: c
101
- }), this.results = new e(/* @__PURE__ */ new Map(), []), this.contents = o || this.defaultContents(r), this.loading = !1, this.error = null, this.duration = null, this.status_code = 200, this.generated_sql = null, this.executed_contents = null, this.storage = a, this.tags = s || [], this.startTime = null, this.cancelCallback = null, this.changed = !0, this.deleted = !1, this.chartFromStatement = !1, this.chartStatementWarnings = [], this.chartLayers = null, this.completionSymbols = [], this.remoteStoreId = c, this.remotePath = a === "remote" ? C(l || this.name, r) : l, this.remoteOriginalPath = u, this.remotePersisted = d;
101
+ }), this.results = new e(/* @__PURE__ */ new Map(), []), this.contents = o || this.defaultContents(r), this.loading = !1, this.error = null, this.duration = null, this.status_code = 200, this.generated_sql = null, this.executed_contents = null, this.storage = a, this.tags = s || [], this.startTime = null, this.cancelCallback = null, this.changed = !0, this.deleted = !1, this.chartFromStatement = !1, this.chartStatementWarnings = [], this.chartLayers = null, this.completionSymbols = [], this.remoteStoreId = c, this.remotePath = a === "remote" ? E(l || this.name, r) : l, this.remoteOriginalPath = u, this.remotePersisted = d;
102
102
  }
103
103
  getAutocomplete(e) {
104
104
  return this.completionSymbols.filter((t) => t.label.startsWith(e));
@@ -110,18 +110,18 @@ var Re = class t {
110
110
  this.contents !== e && (this.contents = e, this.changed = !0);
111
111
  }
112
112
  setName(e) {
113
- let t = this.storage === "remote" ? C(e, this.type) : e;
113
+ let t = this.storage === "remote" ? E(e, this.type) : e;
114
114
  if (this.name === t) return;
115
115
  if (this.changed = !0, this.storage === "remote") {
116
116
  let e = this.remotePath || this.name;
117
117
  !this.remoteOriginalPath && this.remotePersisted && e !== t && (this.remoteOriginalPath = e), this.remotePath = t;
118
118
  }
119
119
  this.name = t;
120
- let n = w(this.name);
120
+ let n = D(this.name);
121
121
  n && this.type !== n && (this.type = n);
122
122
  }
123
123
  setConnection(e) {
124
- this.connection !== e && (this.connection = e, this.connectionId = Le({
124
+ this.connection !== e && (this.connection = e, this.connectionId = Fe({
125
125
  connection: e,
126
126
  storage: this.storage,
127
127
  remoteStoreId: this.remoteStoreId
@@ -189,12 +189,12 @@ var Re = class t {
189
189
  remoteOriginalPath: r.remoteOriginalPath || null,
190
190
  remotePersisted: r.remotePersisted || !1
191
191
  });
192
- return i.syntax = r.syntax || "preql", i.results = r.results ? e.fromJSON(r.results) : new e(/* @__PURE__ */ new Map(), []), i.loading = r.loading || !1, i.error = r.error || null, i.status_code = r.status_code || 200, i.duration = r.duration || null, i.generated_sql = r.generated_sql || null, i.executed_contents = r.executed_contents || null, i.changed = !1, i.tags = r.tags ? r.tags.map((e) => Object.values(E).includes(e) ? e : null).filter((e) => e !== null) : [], i.chartConfig = r.chartConfig || void 0, i.remoteStoreId = r.remoteStoreId || null, i.remotePath = r.remotePath || null, i.remoteOriginalPath = r.remoteOriginalPath || null, i.remotePersisted = r.remotePersisted || !1, i;
192
+ return i.syntax = r.syntax || "preql", i.results = r.results ? e.fromJSON(r.results) : new e(/* @__PURE__ */ new Map(), []), i.loading = r.loading || !1, i.error = r.error || null, i.status_code = r.status_code || 200, i.duration = r.duration || null, i.generated_sql = r.generated_sql || null, i.executed_contents = r.executed_contents || null, i.changed = !1, i.tags = r.tags ? r.tags.map((e) => Object.values(k).includes(e) ? e : null).filter((e) => e !== null) : [], i.chartConfig = r.chartConfig || void 0, i.remoteStoreId = r.remoteStoreId || null, i.remotePath = r.remotePath || null, i.remoteOriginalPath = r.remoteOriginalPath || null, i.remotePersisted = r.remotePersisted || !1, i;
193
193
  }
194
194
  };
195
195
  //#endregion
196
196
  //#region editors/helpers.ts
197
- function ze(e, t, n, r) {
197
+ function Le(e, t, n, r) {
198
198
  let i = [], a = /* @__PURE__ */ new Set(), o = e.reduce((e, t) => (e[t.id] = t, e), {}), s = Object.values(t).sort((e, t) => {
199
199
  let n = e.storage.localeCompare(t.storage);
200
200
  if (n !== 0) return n;
@@ -302,17 +302,17 @@ function ze(e, t, n, r) {
302
302
  }
303
303
  //#endregion
304
304
  //#region llm/overseerAgentPrompt.ts
305
- var D = ["architect", "analyst"], Be = "You are an ANALYST subchat. Your job is to answer the overseer's question — run queries, build charts, surface findings.\n\nWhen done, call return_to_user with a summary written for the person reading the analysis, not for another engineer. Unless your task gives contradicting instructions:\n- Lead with the story in the data: the key insights, what they mean, and any caveats that change how they should be read (data gaps, small samples, definitional quirks).\n- Keep technical details — queries run, tables used, methodology, tool mechanics — out of the top summary. Put anything worth keeping in a short \"Technical notes\" section at the bottom.\n- Focus on the data and the narrative, not the how.", Ve = "You are the OVERSEER of a Trilogy Explorer workspace. Your job is to coordinate work — never to query data directly.\n\nORCHESTRATION RULES:\n- You delegate work to two kinds of subchat: 'architect' (sets up data models from raw files — registers tables, defines Trilogy concepts) and 'analyst' (runs queries, builds charts, produces dashboards).\n- You have NO direct query tools. Always delegate via spawn_subchat.\n- Subchats run asynchronously. spawn_subchat returns immediately with a subchat id and 'running' status. When the subchat finishes, its summary appears in your conversation as a system-injected message and you'll be re-invoked to react.\n- You may follow up with a running OR finished subchat via send_to_subchat — useful for clarifying questions or asking it to extend its work.\n- list_subchats gives you the current snapshot at any time (also visible in the prompt above).\n- When you've handled the user's request and have nothing more to delegate or report, call return_to_user with a concise summary.\n\nDELEGATION GUIDELINES:\n- Architect subchats: use for \"set up a model\", \"load these files\", \"define concepts for X\", schema work.\n- Prefer to keep subchats scoped to a specific goal; only reuse a subchat when the new request overlaps with the existing work.\n- Analyst subchats: use for \"show me revenue trend\", \"build a dashboard\", \"what does the data look like\", any question that produces results or visuals.\n- Give subchats a complete, self-contained task description — they don't see your conversation with the user. Include any relevant file names, connection names, or constraints.\n\nREPORTS (analytical memos):\n- When the user asks for a \"report\", \"memo\", \"writeup\", \"analysis\", or anything that should produce a source-backed narrative deliverable rather than a free-form interactive board, use the create_report tool. This creates a new report-mode dashboard in the active project and queues an initial prompt for the report's dedicated authoring agent.\n- After create_report returns, tell the user the report is open in the sidebar; the dashboard chat panel attached to that report does the authoring and renders the executive memo + claim sections live as the agent works.\n- Do NOT delegate report authoring to an analyst subchat — analysts produce dashboards, the report agent produces narrative memos with provenance and confidence.\n";
306
- function He(e) {
307
- let t = e.lastMessage ? ` — "${Ue(e.lastMessage, 120)}"` : "";
305
+ var Re = ["architect", "analyst"], ze = "You are an ANALYST subchat. Your job is to answer the overseer's question — run queries, build charts, surface findings.\n\nWhen done, call return_to_user with a summary written for the person reading the analysis, not for another engineer. Unless your task gives contradicting instructions:\n- Lead with the story in the data: the key insights, what they mean, and any caveats that change how they should be read (data gaps, small samples, definitional quirks).\n- Keep technical details — queries run, tables used, methodology, tool mechanics — out of the top summary. Put anything worth keeping in a short \"Technical notes\" section at the bottom.\n- Focus on the data and the narrative, not the how.", Be = "You are the OVERSEER of a Trilogy Explorer workspace. Your job is to coordinate work — never to query data directly.\n\nORCHESTRATION RULES:\n- You delegate work to two kinds of subchat: 'architect' (sets up data models from raw files — registers tables, defines Trilogy concepts) and 'analyst' (runs queries, builds charts, produces dashboards).\n- You have NO direct query tools. Always delegate via spawn_subchat.\n- Subchats run asynchronously. spawn_subchat returns immediately with a subchat id and 'running' status. When the subchat finishes, its summary appears in your conversation as a system-injected message and you'll be re-invoked to react.\n- You may follow up with a running OR finished subchat via send_to_subchat — useful for clarifying questions or asking it to extend its work.\n- list_subchats gives you the current snapshot at any time (also visible in the prompt above).\n- When you've handled the user's request and have nothing more to delegate or report, call return_to_user with a concise summary.\n\nDELEGATION GUIDELINES:\n- Architect subchats: use for \"set up a model\", \"load these files\", \"define concepts for X\", schema work.\n- Prefer to keep subchats scoped to a specific goal; only reuse a subchat when the new request overlaps with the existing work.\n- Analyst subchats: use for \"show me revenue trend\", \"build a dashboard\", \"what does the data look like\", any question that produces results or visuals.\n- Give subchats a complete, self-contained task description — they don't see your conversation with the user. Include any relevant file names, connection names, or constraints.\n\nREPORTS (analytical memos):\n- When the user asks for a \"report\", \"memo\", \"writeup\", \"analysis\", or anything that should produce a source-backed narrative deliverable rather than a free-form interactive board, use the create_report tool. This creates a new report-mode dashboard in the active project and queues an initial prompt for the report's dedicated authoring agent.\n- After create_report returns, tell the user the report is open in the sidebar; the dashboard chat panel attached to that report does the authoring and renders the executive memo + claim sections live as the agent works.\n- Do NOT delegate report authoring to an analyst subchat — analysts produce dashboards, the report agent produces narrative memos with provenance and confidence.\n";
306
+ function Ve(e) {
307
+ let t = e.lastMessage ? ` — "${He(e.lastMessage, 120)}"` : "";
308
308
  return ` - ${e.id} [${e.kind}] (${e.status})${t}`;
309
309
  }
310
- function Ue(e, t) {
310
+ function He(e, t) {
311
311
  return e.length <= t ? e : e.slice(0, t - 1) + "…";
312
312
  }
313
- function We(e) {
314
- let { projectName: t, projectDescription: n, subchats: r, availableConnections: i, availableEditors: a, instructionsOverride: o } = e, s = r.length > 0 ? `ACTIVE SUBCHATS:\n${r.map(He).join("\n")}` : "No subchats spawned yet.", c = a.length > 0 ? `Attached files: ${a.join(", ")}` : "No files attached yet.";
315
- return `${o?.trim() || Ve}
313
+ function Ue(e) {
314
+ let { projectName: t, projectDescription: n, subchats: r, availableConnections: i, availableEditors: a, instructionsOverride: o } = e, s = r.length > 0 ? `ACTIVE SUBCHATS:\n${r.map(Ve).join("\n")}` : "No subchats spawned yet.", c = a.length > 0 ? `Attached files: ${a.join(", ")}` : "No files attached yet.";
315
+ return `${o?.trim() || Be}
316
316
 
317
317
  ${`WORKSPACE: ${t || "unnamed"}${n ? `\n${n}` : ""}
318
318
 
@@ -322,7 +322,7 @@ DATA CONNECTIONS: ${i.length > 0 ? i.join(", ") : "none configured"}
322
322
  ${s}`}
323
323
  `;
324
324
  }
325
- var Ge = [
325
+ var We = [
326
326
  {
327
327
  name: "spawn_subchat",
328
328
  description: "Delegate work to a new subchat. Returns immediately with the subchat's id; the subchat runs asynchronously and its summary is injected back into your conversation when it terminates. Use 'architect' for data model setup, 'analyst' for queries / analysis / charts.",
@@ -331,7 +331,7 @@ var Ge = [
331
331
  properties: {
332
332
  kind: {
333
333
  type: "string",
334
- enum: D,
334
+ enum: Re,
335
335
  description: "'architect' for data model / schema work; 'analyst' for queries, analysis, and charts."
336
336
  },
337
337
  task: {
@@ -415,21 +415,21 @@ var Ge = [
415
415
  required: ["subchat_id"]
416
416
  }
417
417
  },
418
- v
418
+ y
419
419
  ];
420
420
  //#endregion
421
421
  //#region llm/dashboardAgentTools.ts
422
- function O(e, t) {
422
+ function A(e, t) {
423
423
  return {
424
424
  type: "string",
425
425
  enum: e.map((e) => e.value),
426
426
  description: `${t} ${e.map((e) => `"${e.value}": ${e.hint}`).join(" ")}`
427
427
  };
428
428
  }
429
- var Ke = Object.fromEntries(ne.map((e) => [e.key, {
429
+ var Ge = Object.fromEntries(d.map((e) => [e.key, {
430
430
  type: "string",
431
431
  description: `${e.label} color — ${e.hint} Accepts a hex string ("#101820"), rgb()/rgba()/hsl(), or a CSS color name. Pass an empty string to clear it back to the inherited theme color. Omit to leave it as-is.`
432
- }])), qe = [
432
+ }])), j = [
433
433
  {
434
434
  name: "get_dashboard_state",
435
435
  description: "Re-read the current state of the dashboard: title, description, active data connection, active data source, item list, and (in report mode) section structure. The dashboard state in your starting context is a snapshot from the beginning of the conversation and does NOT update as you work — call this after making changes, or whenever you are unsure whether your view is current. Pass item_id to read just one item instead of the whole dashboard.",
@@ -465,7 +465,7 @@ var Ke = Object.fromEntries(ne.map((e) => [e.key, {
465
465
  },
466
466
  {
467
467
  name: "add_dashboard_item",
468
- description: `Add a new item to the dashboard grid. Supports chart, table, markdown, filter, and freeform types. For charts and tables, provide a Trilogy query as content. For markdown items you can ALSO supply a 'query' to drive dynamic data — the query results are then available to the markdown via {field} template substitutions (see system prompt for full templating syntax). For freeform items, put the Trilogy query in 'content' and the widget markup in 'html' — the query and the rendering are authored separately, and the widget never issues queries of its own. ${Te}`,
468
+ description: `Add a new item to the dashboard grid. Supports chart, table, markdown, filter, and freeform types. For charts and tables, provide a Trilogy query as content. For markdown items you can ALSO supply a 'query' to drive dynamic data — the query results are then available to the markdown via {field} template substitutions (see system prompt for full templating syntax). For freeform items, put the Trilogy query in 'content' and the widget markup in 'html' — the query and the rendering are authored separately, and the widget never issues queries of its own. ${ae}`,
469
469
  input_schema: {
470
470
  type: "object",
471
471
  properties: {
@@ -497,7 +497,7 @@ var Ke = Object.fromEntries(ne.map((e) => [e.key, {
497
497
  description: "Optional Trilogy query for MARKDOWN items only. When provided, the query is executed and its result rows are bound to the markdown template — you can reference fields with {field}, {data[N].field}, formats like {revenue:,.2f}, fallbacks like {field || \"N/A\"}, and loops {{#each data}}...{{/each}}. Ignored for non-markdown types."
498
498
  },
499
499
  chartConfig: {
500
- ...ce,
500
+ ...se,
501
501
  description: "Chart configuration (only for chart type). If omitted, chart type is auto-detected from the data."
502
502
  },
503
503
  width: {
@@ -550,7 +550,7 @@ var Ke = Object.fromEntries(ne.map((e) => [e.key, {
550
550
  description: "Change the item type"
551
551
  },
552
552
  chartConfig: {
553
- ...ce,
553
+ ...se,
554
554
  description: "New chart configuration (for chart items)"
555
555
  }
556
556
  },
@@ -644,15 +644,15 @@ var Ke = Object.fromEntries(ne.map((e) => [e.key, {
644
644
  input_schema: {
645
645
  type: "object",
646
646
  properties: {
647
- preset: O(re, "Baseline look. Sets corners, density, elevation, and border treatment together; the individual fields below override whatever it chose."),
648
- corners: O(ee, "Card corner rounding."),
649
- density: O(d, "Gutter width and padding. Denser fits more panels per screen; spacious suits fewer, larger panels."),
650
- elevation: O(l, "Card drop shadow."),
647
+ preset: A(te, "Baseline look. Sets corners, density, elevation, and border treatment together; the individual fields below override whatever it chose."),
648
+ corners: A(a, "Card corner rounding."),
649
+ density: A(i, "Gutter width and padding. Denser fits more panels per screen; spacious suits fewer, larger panels."),
650
+ elevation: A(h, "Card drop shadow."),
651
651
  mobileCards: {
652
652
  type: "boolean",
653
653
  description: "Keep the card treatment on narrow viewports. Default false, which flattens panels to the page background below 768px — the right choice for most dashboards, since cards on a phone waste horizontal space."
654
654
  },
655
- ...Ke,
655
+ ...Ge,
656
656
  reset: {
657
657
  type: "boolean",
658
658
  description: "Clear the entire theme and go back to the app default, which follows the user's light/dark setting. Use this when the user says \"undo the styling\" or \"back to normal\". When true, every other field is ignored."
@@ -709,9 +709,9 @@ var Ke = Object.fromEntries(ne.map((e) => [e.key, {
709
709
  required: []
710
710
  }
711
711
  },
712
- ue,
713
- v
714
- ], Je = [
712
+ _e,
713
+ y
714
+ ], Ke = [
715
715
  {
716
716
  name: "set_executive_memo",
717
717
  description: "Create or update the executive memo at the top of the report. Exactly one memo per report. The six fields ARE the memo — they map to the headline / verdict / magnitude / cause / action / confidence rubric. Pass `query` to bind live values into the prose via {field} templating. Calling this when a memo already exists updates it in place.",
@@ -842,35 +842,35 @@ var Ke = Object.fromEntries(ne.map((e) => [e.key, {
842
842
  ];
843
843
  //#endregion
844
844
  //#region llm/dashboardAgentPrompt.ts
845
- function Ye(e) {
846
- let { dashboard: t, dataConnectionName: r, activeImports: i = [], isDataConnectionActive: a = !0, itemId: o } = e, s = Object.entries(t.gridItems);
847
- if (o) {
848
- let e = t.gridItems[o];
845
+ function qe(e) {
846
+ let { dashboard: t, dataConnectionName: n, activeImports: r = [], isDataConnectionActive: i = !0, itemId: a } = e, o = Object.entries(t.gridItems);
847
+ if (a) {
848
+ let e = t.gridItems[a];
849
849
  if (!e) {
850
- let e = s.map(([e]) => e).join(", ");
851
- return `Item "${o}" not found on this dashboard.${e ? ` Known item IDs: ${e}` : " The dashboard is empty."}`;
850
+ let e = o.map(([e]) => e).join(", ");
851
+ return `Item "${a}" not found on this dashboard.${e ? ` Known item IDs: ${e}` : " The dashboard is empty."}`;
852
852
  }
853
- return `CURRENT STATE OF ITEM [${o}]:\n${Xe(o, e)}`;
853
+ return `CURRENT STATE OF ITEM [${a}]:\n${Je(a, e)}`;
854
854
  }
855
- let c = r ? `ACTIVE DATA CONNECTION: ${r}${r && !a ? " (NOT CONNECTED - use connect_data_connection tool to connect before running queries)" : ""}` : "ACTIVE DATA CONNECTION: none selected. Ask the user which connection to use.", l = i.length > 0 ? `ACTIVE DATA SOURCE: ${i[0].name} (this is the import the dashboard itself uses — changing it via select_active_import will affect every existing item on the next refresh)` : "ACTIVE DATA SOURCE: none selected. Use select_active_import to select a data source, or list_available_imports to see what is available.", u = s.length > 0 ? `CURRENT DASHBOARD ITEMS (${s.length}):\n${s.map(([e, t]) => Xe(e, t)).join("\n")}` : "The dashboard is currently empty.", d = t.layoutType === "report" ? `\n${Ze(t)}` : "";
855
+ let s = n ? `ACTIVE DATA CONNECTION: ${n}${n && !i ? " (NOT CONNECTED - use connect_data_connection tool to connect before running queries)" : ""}` : "ACTIVE DATA CONNECTION: none selected. Ask the user which connection to use.", c = r.length > 0 ? `ACTIVE DATA SOURCE: ${r[0].name} (this is the import the dashboard itself uses — changing it via select_active_import will affect every existing item on the next refresh)` : "ACTIVE DATA SOURCE: none selected. Use select_active_import to select a data source, or list_available_imports to see what is available.", l = o.length > 0 ? `CURRENT DASHBOARD ITEMS (${o.length}):\n${o.map(([e, t]) => Je(e, t)).join("\n")}` : "The dashboard is currently empty.", u = t.layoutType === "report" ? `\n${Ye(t)}` : "";
856
856
  return `DASHBOARD: "${t.name}"${t.description ? `\nDESCRIPTION: ${t.description}` : ""}
857
+ ${s}
857
858
  ${c}
858
- ${l}
859
- THEME: ${n(t.theme)}${d}
859
+ THEME: ${x(t.theme)}${u}
860
860
 
861
- ${u}`;
861
+ ${l}`;
862
862
  }
863
- function Xe(e, t) {
863
+ function Je(e, t) {
864
864
  let n = typeof t.content == "string" ? t.content.substring(0, 80) : t.content?.markdown?.substring(0, 80) || "";
865
865
  return `- [${e}] ${t.type}: "${t.name}" — ${n}${n.length >= 80 ? "..." : ""}`;
866
866
  }
867
- function Ze(e) {
868
- let t = Object.values(e.gridItems), n = t.filter((e) => e.type === a.MEMO).length, r = t.filter((e) => e.type === a.CLAIM).length, i = t.filter((e) => e.type === a.APPENDIX_HEADER).length;
867
+ function Ye(e) {
868
+ let t = Object.values(e.gridItems), n = t.filter((e) => e.type === l.MEMO).length, r = t.filter((e) => e.type === l.CLAIM).length, i = t.filter((e) => e.type === l.APPENDIX_HEADER).length;
869
869
  return `CURRENT REPORT STRUCTURE: ${n} memo / ${r} claim section${r === 1 ? "" : "s"} / ${i} appendix divider${i === 1 ? "" : "s"}.`;
870
870
  }
871
- function Qe(e) {
872
- let { dashboard: t, availableConnections: n, availableConcepts: r, availableImportsForConnection: i = [] } = e, a = r && r.length > 0 ? `\n\nAVAILABLE FIELDS FOR QUERIES:\n${be(r)}` : "", o = i.length > 0 ? `\n\nAVAILABLE DATA SOURCES:\n${i.map((e) => `- ${e.name}`).join("\n")}` : "", s = t.layoutType === "report";
873
- return `${s ? et() : ""}You are a dashboard assistant with full control over a live ${s ? "report (a single-column analytical memo)" : "dashboard"}. The user can see changes you make in real time.
871
+ function Xe(e) {
872
+ let { dashboard: t, availableConnections: n, availableConcepts: r, availableImportsForConnection: i = [] } = e, a = r && r.length > 0 ? `\n\nAVAILABLE FIELDS FOR QUERIES:\n${pe(r)}` : "", o = i.length > 0 ? `\n\nAVAILABLE DATA SOURCES:\n${i.map((e) => `- ${e.name}`).join("\n")}` : "", s = t.layoutType === "report";
873
+ return `${s ? Qe() : ""}You are a dashboard assistant with full control over a live ${s ? "report (a single-column analytical memo)" : "dashboard"}. The user can see changes you make in real time.
874
874
 
875
875
  ${s ? "This is a REPORT, not a free-form dashboard. Reports render top-to-bottom as a single narrative column. Use the report tools (set_executive_memo, add_claim_section, add_appendix_header) to author structure; use add_dashboard_item only for evidence (charts, tables, markdown). The grid x/y/w/h still drives ordering — y determines vertical position; x and w are mostly cosmetic in report mode." : "You have tools to add, update, remove, and reposition items on the dashboard grid. The grid is 20 columns wide. Items can be charts, tables, markdown blocks, filters, or freeform custom-HTML widgets."}
876
876
 
@@ -880,7 +880,7 @@ The current dashboard title, items, active connection, and active data source ar
880
880
  AVAILABLE DATA CONNECTIONS: ${n.length > 0 ? n.join(", ") : "None configured"}${o}
881
881
 
882
882
  TRILOGY LANGUAGE REFERENCE:
883
- ${x}
883
+ ${S}
884
884
  ${a}
885
885
 
886
886
  IMPORTANT GUIDELINES:
@@ -946,7 +946,7 @@ Example loop over top products:
946
946
 
947
947
  When the user asks for a "summary card", "KPI tile", "headline metric", or any narrative text that should reflect live data, prefer a markdown item with a query over hardcoded numbers.
948
948
 
949
- ${$e()}
949
+ ${Ze()}
950
950
 
951
951
  VALIDATION BEFORE HANDOFF:
952
952
  - Once you believe the dashboard is complete, call capture_dashboard_screenshot to render it as a PNG and review the actual visual layout.
@@ -958,25 +958,25 @@ VALIDATION BEFORE HANDOFF:
958
958
  COMPLETING YOUR RESPONSE:
959
959
  - When you have finished addressing the user's request — and have validated the result via capture_dashboard_screenshot — call return_to_user with a brief summary.
960
960
  - Never end a turn with plain text only — you must always call a tool. return_to_user is always your final tool call.
961
- ${s ? tt() : ""}`;
961
+ ${s ? $e() : ""}`;
962
962
  }
963
- function $e() {
963
+ function Ze() {
964
964
  return "FREEFORM WIDGETS (custom HTML):\nWhen the built-in chart types genuinely cannot express what the user asked for — a bespoke layout, a custom interaction, a visual metaphor specific to their domain — add an item of type \"freeform\". Prefer a chart or table when one fits: they are themed, accessible, and maintained. Reach for a widget when they don't.\n\nAuthoring a widget is TWO separate artifacts, passed as two fields:\n- `content`: the Trilogy query. Written exactly like a chart query, and subject to the same rules (full field paths, LIMIT, no GROUP BY). Global filters and cross-filters apply to it automatically.\n- `html`: the markup that renders the query's rows. You never write queries inside the widget — the widget cannot issue them.\n\nThe widget runs inside a sandboxed iframe. Available to it:\n- `trilogy.subscribe(fn)` — fn is called immediately and on every change with `{ status, columns, rows, rowCount, truncated, filters, error }`. `status` is 'loading' | 'ready' | 'error'; `columns` is `[{ name, type, address }]`; `rows` is an array of plain objects keyed by column name.\n- `trilogy.ready()` — REQUIRED. Call it once the first render completes. A widget that never calls ready() is reported as broken after a few seconds.\n- `trilogy.filters.eq(field, value)` — cross-filter the rest of the dashboard, exactly like clicking a chart mark. Pass the column name as it appears in `rows` (e.g. `'product.name'`); the host resolves it to the underlying concept. A field the dashboard doesn't expose is ignored.\n- `trilogy.filters.set(map)` / `.append(map)` / `.clear()` — map is `{ field: { op: 'eq' | 'in' | 'range' | 'is_null', value } }`.\n- `trilogy.refresh()` — re-run the backing query.\n- `trilogy.resize(px)` — request a taller frame.\n- `trilogy.theme` — `{ mode, vars }` where mode is 'light' or 'dark'. See the theme contract below.\n\nTHEME CONTRACT (read this before writing any CSS):\nNEVER hardcode a color, background, or font in a widget. The dashboard runs in light OR dark mode, the user can switch at any time, and a widget with baked-in colors is illegible in the other mode. Every color you need is already injected as a CSS variable on `:root` inside the frame, and they are re-pushed when the user switches mode:\n- `--widget-bg` — page background. The frame is transparent over the dashboard card by default; only set a background if you need a distinct surface.\n- `--widget-surface` — a raised/inset surface (cards, table headers, wells).\n- `--widget-text` — primary text. `--widget-text-muted` — secondary/labels/axis text.\n- `--widget-border` — borders and dividers. `--widget-border-light` — subtler dividers.\n- `--widget-accent` — accent/primary. `--widget-accent-rgb` — the same color as \"r, g, b\" for `rgba(var(--widget-accent-rgb), 0.12)` tints.\n- `--widget-positive` / `--widget-negative` — good/bad, up/down, profit/loss.\n- `--widget-series-1` … `--widget-series-6` — categorical palette for multi-series charts. Use these in order; do not invent your own.\n- `--widget-font`, `--widget-font-size` — already applied to `body`; inherit rather than restating.\n\nRules:\n1. Use `var(--widget-*)` for EVERY color. No hex literals, no `rgb()` literals, no named colors, no `background: white`, no `color: #333`.\n2. For translucent fills use `rgba(var(--widget-accent-rgb), 0.12)` — never a hardcoded rgba.\n3. Do not set `background` on `body` or `html`. The frame is transparent so the widget sits on the dashboard card; painting your own background is what makes a widget look like a pasted-in rectangle.\n4. If you truly need a mode-specific value, branch on `trilogy.theme.mode` inside `subscribe` — do not assume dark.\n5. Test your reasoning against both modes before finishing: would every piece of text still be readable if the background flipped?\n\nSandbox limits — code that violates these silently fails:\n- No network. `fetch`, XHR, WebSockets, and remote images are blocked. All data comes from the query.\n- No storage. `localStorage`, `sessionStorage`, and cookies are unavailable.\n- No access to the host page, no popups, no forms, no top-level navigation.\n- Libraries may be loaded from cdn.jsdelivr.net, unpkg.com, esm.sh, or cdnjs.cloudflare.com. Pin exact versions. Prefer plain DOM — it always works and loads instantly.\n\nWidget authoring rules:\n1. Handle every status. Render something sensible while `status === 'loading'` and when `rows` is empty; never assume data has arrived on first call.\n2. Read column names from `columns`, or use the names your own query selected — do not hardcode names you did not select.\n3. Build DOM with `textContent`/`createElement` rather than string concatenation into `innerHTML`. Row values are data you do not control, and a value containing markup will otherwise break the layout.\n4. Keep it self-contained: one `<style>`, one `<script>`, no build step.\n5. Call `trilogy.ready()` at the end of the initial render, not inside the subscribe callback's data branch.\n6. Every color comes from the theme contract above. A widget that hardcodes colors is a defect, not a style preference.\n\nMinimal shape:\n content:\n SELECT product.name, product.revenue ORDER BY product.revenue DESC LIMIT 10;\n html:\n <style>\n .row { padding: 6px 8px; border-bottom: 1px solid var(--widget-border-light); cursor: pointer; }\n .row:hover { background: rgba(var(--widget-accent-rgb), 0.08); }\n .name { color: var(--widget-text); }\n .value { color: var(--widget-text-muted); font-variant-numeric: tabular-nums; }\n .empty { color: var(--widget-text-muted); padding: 12px; }\n </style>\n <div id=\"root\"></div>\n <script>\n var root = document.getElementById('root');\n trilogy.subscribe(function (state) {\n if (!state.rows.length) {\n root.innerHTML = '';\n var empty = document.createElement('div');\n empty.className = 'empty';\n empty.textContent = state.status === 'loading' ? 'Loading…' : 'No data';\n root.appendChild(empty);\n return;\n }\n root.innerHTML = '';\n state.rows.forEach(function (row) {\n var el = document.createElement('div');\n el.className = 'row';\n var name = document.createElement('span');\n name.className = 'name';\n name.textContent = row['product.name'];\n var value = document.createElement('span');\n value.className = 'value';\n value.textContent = ' — ' + row['product.revenue'];\n el.appendChild(name);\n el.appendChild(value);\n el.onclick = function () { trilogy.filters.eq('product.name', row['product.name']); };\n root.appendChild(el);\n });\n });\n trilogy.ready();\n <\/script>";
965
965
  }
966
- function et() {
966
+ function Qe() {
967
967
  return "# REPORT AUTHORING MODE\n\nYou are authoring a source-backed analytical memo, not an interactive dashboard. The reader is a busy decision-maker who wants the answer, the evidence, and the honest caveats — in that order.\n\nPRINCIPLES (non-negotiable):\n1. Make the claim. A great agent makes a claim, shows the evidence, exposes uncertainty, and lets the human interrogate it. Do NOT defer to the reader to make the call themselves by laying out raw charts.\n2. Executive memo first. One memo at the top: headline / verdict / magnitude / cause / action / confidence. Use set_executive_memo. Aim for one screen — the reader should not need to scroll to get the answer.\n3. Narrative sections, not dashboard tiles. Each claim is its own section. Do NOT title sections \"Revenue Chart\" or \"Conversion Analysis\" — those are table-of-contents entries, not insights. Title them with the claim itself.\n4. Charts serve the argument. Each claim has exactly the visual evidence needed to support it — no more. Add a chart RIGHT AFTER its claim section so the report renderer pairs them visually.\n5. Caveats are mandatory on every claim. State what the data does NOT prove. If you cannot articulate a caveat, the claim is probably overconfident.\n6. Drilldown comes after the answer, not before. Interactivity belongs below the verdict — never use it as the primary communication layer.\n7. Tables go in the appendix. Use add_appendix_header before adding detail tables; never put a giant table above a claim.\n8. Provenance is the price of authorship. Every claim must have a query, filters, and a confidence level. Use get_provenance to audit your own evidence chain before handing off. If you can't justify a number, do not state it.\n\n";
968
968
  }
969
- function tt() {
969
+ function $e() {
970
970
  return "\n\nREPORT AUTHORING WORKFLOW:\n1. Start with set_executive_memo. All six fields are required. Pick a verdict (\"good\" / \"bad\" / \"mixed\" / \"inconclusive\") — pick \"inconclusive\" only when the data genuinely cannot decide. Pick a confidence (\"high\" / \"medium\" / \"low\") and explain the rationale. Bind a query if your magnitude/cause/action want live numbers.\n2. For each point you want to make, call add_claim_section with the claim sentence (and caveat, drilldown). Then immediately call add_dashboard_item with type='chart' (or 'table') for the evidence — the renderer pairs adjacent blocks. Keep one claim per section.\n3. When you have detail tables or methodology, call add_appendix_header once, then add_dashboard_item for each appendix block. Anything added after the divider renders as appendix content.\n4. Audit before handing off: call get_provenance on at least your memo and each claim section. Confirm queries, filters, and confidence are sane.\n5. Capture a screenshot to review visual rendering (capture_dashboard_screenshot).\n6. Call return_to_user with a one-paragraph summary highlighting the verdict and your confidence.\n\nANTI-PATTERNS (don't):\n- A wall of charts with no claims. That is the defensive design we are explicitly avoiding.\n- A claim without a chart, OR a chart without a claim. They come in pairs.\n- \"Tabs\" or interactivity-driven analysis. Reports are read top-to-bottom. Use drilldown text on claims when the reader needs to investigate further.\n- Memos with vague verdicts (\"worth investigating\", \"needs more analysis\"). Pick a side; lower the confidence if you are unsure.\n";
971
971
  }
972
972
  //#endregion
973
973
  //#region dashboards/connectionResolution.ts
974
- function k(e, t) {
974
+ function M(e, t) {
975
975
  if (!e) return "";
976
976
  let { connection: n = "", connectionId: r = "" } = e;
977
977
  return r && t.connections[r] ? r : (n ? t.connectionByName(n)?.id : void 0) || r || n;
978
978
  }
979
- function nt(e, t) {
979
+ function et(e, t) {
980
980
  let n = 0;
981
981
  for (let r of e) {
982
982
  if (r.connectionId && t.connections[r.connectionId] || !r.connection) continue;
@@ -987,47 +987,47 @@ function nt(e, t) {
987
987
  }
988
988
  //#endregion
989
989
  //#region dashboards/freeform/types.ts
990
- var rt = 262144, it = 2e4, at = 8e3, ot = 4e3, st = [
990
+ var tt = 262144, nt = 2e4, rt = 8e3, it = 4e3, at = [
991
991
  "https://cdn.jsdelivr.net",
992
992
  "https://unpkg.com",
993
993
  "https://esm.sh",
994
994
  "https://cdnjs.cloudflare.com"
995
- ], ct = "trilogy-freeform-hello";
995
+ ], ot = "trilogy-freeform-hello";
996
996
  //#endregion
997
997
  //#region dashboards/provenance.ts
998
- function lt(e, t, n) {
999
- let i = m(t), o = Array.isArray(n.imports) && n.imports.length > 0 ? n.imports[0] : void 0, s = [];
1000
- n.filter && s.push({
998
+ function st(e, t, r) {
999
+ let i = n(t), a = Array.isArray(r.imports) && r.imports.length > 0 ? r.imports[0] : void 0, o = [];
1000
+ r.filter && o.push({
1001
1001
  source: "global",
1002
- expression: n.filter
1002
+ expression: r.filter
1003
1003
  });
1004
- for (let e of t.filters || []) s.push({
1004
+ for (let e of t.filters || []) o.push({
1005
1005
  source: e.source,
1006
1006
  expression: e.value,
1007
1007
  parameters: e.parameters
1008
1008
  });
1009
- let c, l, u, d;
1010
- pe(t.content) ? (c = t.content.confidence, l = t.content.confidenceRationale) : me(t.content) && (u = t.content.caveat, d = t.content.drilldown);
1011
- let f = !i && !(t.type === a.CHART || t.type === a.TABLE) && !(r(t.content) && t.content.query);
1009
+ let u, d, f, p;
1010
+ c(t.content) ? (u = t.content.confidence, d = t.content.confidenceRationale) : ue(t.content) && (f = t.content.caveat, p = t.content.drilldown);
1011
+ let m = !i && !(t.type === l.CHART || t.type === l.TABLE) && !(s(t.content) && t.content.query);
1012
1012
  return {
1013
1013
  itemId: e,
1014
1014
  type: t.type,
1015
1015
  name: t.name,
1016
1016
  query: i,
1017
- dataSource: o?.name,
1018
- freshness: n.updatedAt,
1019
- filters: s,
1017
+ dataSource: a?.name,
1018
+ freshness: r.updatedAt,
1019
+ filters: o,
1020
1020
  parameters: t.parameters,
1021
- confidence: c,
1022
- confidenceRationale: l,
1023
- caveat: u,
1024
- drilldown: d,
1021
+ confidence: u,
1022
+ confidenceRationale: d,
1023
+ caveat: f,
1024
+ drilldown: p,
1025
1025
  rowCount: t.results?.data?.length,
1026
1026
  error: t.error ?? null,
1027
- noQuery: f
1027
+ noQuery: m
1028
1028
  };
1029
1029
  }
1030
- function ut(e) {
1030
+ function ct(e) {
1031
1031
  let t = [];
1032
1032
  if (t.push(`# Provenance for "${e.name}" [${e.itemId}] (${e.type})`), t.push(""), e.dataSource && t.push(`- Data source: ${e.dataSource}`), t.push(`- Last updated: ${e.freshness.toISOString()}`), e.confidence && t.push(`- Confidence: ${e.confidence}${e.confidenceRationale ? ` — ${e.confidenceRationale}` : ""}`), e.rowCount !== void 0 && t.push(`- Rows returned: ${e.rowCount}`), e.error && t.push(`- Last error: ${e.error}`), e.caveat && t.push(`- Caveat: ${e.caveat}`), e.drilldown && t.push(`- Next drilldown: ${e.drilldown}`), e.filters.length > 0) {
1033
1033
  t.push("- Filters:");
@@ -1042,14 +1042,14 @@ function ut(e) {
1042
1042
  }
1043
1043
  //#endregion
1044
1044
  //#region llm/dashboardToolExecutor.ts
1045
- function dt(e) {
1046
- return !e || !e.trim() ? "Freeform items require `html` — the widget markup. Author the query in `content` and the markup in `html`." : e.length > 262144 ? `Widget html is ${e.length} characters; the limit is ${rt}. Simplify the widget or move logic into fewer, denser helpers.` : null;
1045
+ function lt(e) {
1046
+ return !e || !e.trim() ? "Freeform items require `html` — the widget markup. Author the query in `content` and the markup in `html`." : e.length > 262144 ? `Widget html is ${e.length} characters; the limit is ${tt}. Simplify the widget or move logic into fewer, denser helpers.` : null;
1047
1047
  }
1048
- var ft = { heatmap: {
1048
+ var ut = { heatmap: {
1049
1049
  xField: 25,
1050
1050
  yField: 25
1051
1051
  } };
1052
- function pt(e) {
1052
+ function dt(e) {
1053
1053
  if (e == null) return null;
1054
1054
  if (typeof e == "object") {
1055
1055
  let t = e;
@@ -1057,15 +1057,15 @@ function pt(e) {
1057
1057
  }
1058
1058
  return String(e);
1059
1059
  }
1060
- function mt(e, t) {
1060
+ function ft(e, t) {
1061
1061
  let n = /* @__PURE__ */ new Set();
1062
1062
  for (let r of e) {
1063
- let e = pt(r[t]);
1063
+ let e = dt(r[t]);
1064
1064
  e !== null && n.add(e);
1065
1065
  }
1066
1066
  return n.size;
1067
1067
  }
1068
- var ht = class {
1068
+ var pt = class {
1069
1069
  deps;
1070
1070
  constructor(e) {
1071
1071
  this.deps = e;
@@ -1074,7 +1074,7 @@ var ht = class {
1074
1074
  return this.deps.dashboardStore.dashboards[this.deps.dashboardId] || null;
1075
1075
  }
1076
1076
  get connectionName() {
1077
- return k(this.dashboard, this.deps.connectionStore);
1077
+ return M(this.dashboard, this.deps.connectionStore);
1078
1078
  }
1079
1079
  async executeToolCall(e, t) {
1080
1080
  switch (e) {
@@ -1119,7 +1119,7 @@ var ht = class {
1119
1119
  let n = this.deps.connectionStore?.connections[this.connectionName];
1120
1120
  return {
1121
1121
  success: !0,
1122
- message: Ye({
1122
+ message: qe({
1123
1123
  dashboard: t,
1124
1124
  dataConnectionName: t.connection,
1125
1125
  activeImports: this.deps.getActiveImports(),
@@ -1140,8 +1140,8 @@ var ht = class {
1140
1140
  message: "The dashboard is empty. No items to list."
1141
1141
  };
1142
1142
  let n = new Map(e.layout.map((e) => [e.i, e])), r = t.map(([e, t]) => {
1143
- let r = n.get(e), i = typeof t.content == "object" && t.content !== null, o = i ? t.content : null, s = t.type === a.FREEFORM && i ? t.content : null, c = s ? `${s.html.length} chars of widget html` : i ? o?.markdown?.substring(0, 120) || "" : t.content.substring(0, 120), l = r ? `(x=${r.x}, y=${r.y}, w=${r.w}, h=${r.h})` : "", u = s?.query || o?.query, d = u ? `\n Query: ${u.substring(0, 120)}${u.length >= 120 ? "..." : ""}` : "";
1144
- return `- [${e}] ${t.type}: "${t.name}" ${l}\n Content: ${c}${c.length >= 120 ? "..." : ""}${d}`;
1143
+ let r = n.get(e), i = typeof t.content == "object" && t.content !== null, a = i ? t.content : null, o = t.type === l.FREEFORM && i ? t.content : null, s = o ? `${o.html.length} chars of widget html` : i ? a?.markdown?.substring(0, 120) || "" : t.content.substring(0, 120), c = r ? `(x=${r.x}, y=${r.y}, w=${r.w}, h=${r.h})` : "", u = o?.query || a?.query, d = u ? `\n Query: ${u.substring(0, 120)}${u.length >= 120 ? "..." : ""}` : "";
1144
+ return `- [${e}] ${t.type}: "${t.name}" ${c}\n Content: ${s}${s.length >= 120 ? "..." : ""}${d}`;
1145
1145
  });
1146
1146
  return {
1147
1147
  success: !0,
@@ -1149,42 +1149,42 @@ var ht = class {
1149
1149
  };
1150
1150
  }
1151
1151
  getDashboardItem(e) {
1152
- let n = this.dashboard;
1153
- if (!n) return {
1152
+ let t = this.dashboard;
1153
+ if (!t) return {
1154
1154
  success: !1,
1155
1155
  error: "Dashboard not found."
1156
1156
  };
1157
- let r = n.gridItems[e];
1158
- if (!r) return {
1157
+ let n = t.gridItems[e];
1158
+ if (!n) return {
1159
1159
  success: !1,
1160
1160
  error: `Item "${e}" not found. Use list_dashboard_items to see available items.`
1161
1161
  };
1162
- let i = n.layout.find((t) => t.i === e), o = typeof r.content == "object" && r.content !== null, s = o ? r.content : null, c = r.type === a.FREEFORM && o ? r.content : null, l = {
1162
+ let r = t.layout.find((t) => t.i === e), i = typeof n.content == "object" && n.content !== null, a = i ? n.content : null, o = n.type === l.FREEFORM && i ? n.content : null, s = {
1163
1163
  id: e,
1164
- type: r.type,
1165
- name: r.name,
1166
- content: c ? c.query : o ? s?.markdown || "" : r.content,
1167
- query: c?.query || s?.query || null,
1168
- chartConfig: r.chartConfig || null,
1169
- position: i ? {
1170
- x: i.x,
1171
- y: i.y,
1172
- w: i.w,
1173
- h: i.h
1164
+ type: n.type,
1165
+ name: n.name,
1166
+ content: o ? o.query : i ? a?.markdown || "" : n.content,
1167
+ query: o?.query || a?.query || null,
1168
+ chartConfig: n.chartConfig || null,
1169
+ position: r ? {
1170
+ x: r.x,
1171
+ y: r.y,
1172
+ w: r.w,
1173
+ h: r.h
1174
1174
  } : null,
1175
- loading: r.loading || !1,
1176
- error: r.error || null
1175
+ loading: n.loading || !1,
1176
+ error: n.error || null
1177
1177
  };
1178
- if (c && (l.html = c.html), r.results) {
1179
- let e = typeof r.results.toJSON == "function" ? r.results.toJSON() : r.results, { head: n, totalRows: i, cutCount: a } = t(e);
1180
- a > 0 ? (l.resultPreview = {
1178
+ if (o && (s.html = o.html), n.results) {
1179
+ let e = typeof n.results.toJSON == "function" ? n.results.toJSON() : n.results, { head: t, totalRows: r, cutCount: i } = oe(e);
1180
+ i > 0 ? (s.resultPreview = {
1181
1181
  ...e,
1182
- data: n
1183
- }, l.resultTruncated = `Showing first ${n.length} of ${i} rows`) : l.results = e;
1182
+ data: t
1183
+ }, s.resultTruncated = `Showing first ${t.length} of ${r} rows`) : s.results = e;
1184
1184
  }
1185
1185
  return {
1186
1186
  success: !0,
1187
- message: JSON.stringify(l, null, 2)
1187
+ message: JSON.stringify(s, null, 2)
1188
1188
  };
1189
1189
  }
1190
1190
  async validateChartConfigAfterQuery(e, t, n) {
@@ -1193,10 +1193,10 @@ var ht = class {
1193
1193
  try {
1194
1194
  let i = await r.waitForQuery(t), a = i?.results ?? i ?? null;
1195
1195
  if (!a || !a.headers) return null;
1196
- let o = p(a.data, a.headers, n);
1196
+ let o = v(a.data, a.headers, n);
1197
1197
  if (o.valid) return null;
1198
1198
  let s = o.suggestedConfig;
1199
- return s && s.chartType && this.deps.dashboardStore.updateItemChartConfig(this.deps.dashboardId, e, s), `Auto-corrected to default configuration.\n${b(o)}`;
1199
+ return s && s.chartType && this.deps.dashboardStore.updateItemChartConfig(this.deps.dashboardId, e, s), `Auto-corrected to default configuration.\n${C(o)}`;
1200
1200
  } catch {
1201
1201
  return null;
1202
1202
  }
@@ -1223,34 +1223,34 @@ var ht = class {
1223
1223
  success: !1,
1224
1224
  error: "Content is required."
1225
1225
  };
1226
- let i = n, o = typeof e.query == "string" ? e.query : "", s = i === a.MARKDOWN && o.length > 0, c = i === a.FREEFORM, l = typeof e.html == "string" ? e.html : "";
1227
- if (c) {
1228
- let e = dt(l);
1226
+ let i = n, a = typeof e.query == "string" ? e.query : "", o = i === l.MARKDOWN && a.length > 0, s = i === l.FREEFORM, c = typeof e.html == "string" ? e.html : "";
1227
+ if (s) {
1228
+ let e = lt(c);
1229
1229
  if (e) return {
1230
1230
  success: !1,
1231
1231
  error: e
1232
1232
  };
1233
1233
  }
1234
- let u = e.width || (i === a.MARKDOWN ? 20 : 10), d = e.height || (i === a.MARKDOWN ? s ? 6 : 4 : 8), f = e.name || n.charAt(0).toUpperCase() + n.slice(1), p = t.layout.reduce((e, t) => Math.max(e, t.y + t.h), 0), m = this.deps.dashboardStore.addItemToDashboard(this.deps.dashboardId, i, 0, p, u, d, r, f);
1235
- if (s) {
1234
+ let u = e.width || (i === l.MARKDOWN ? 20 : 10), d = e.height || (i === l.MARKDOWN ? o ? 6 : 4 : 8), f = e.name || n.charAt(0).toUpperCase() + n.slice(1), p = t.layout.reduce((e, t) => Math.max(e, t.y + t.h), 0), m = this.deps.dashboardStore.addItemToDashboard(this.deps.dashboardId, i, 0, p, u, d, r, f);
1235
+ if (o) {
1236
1236
  let e = {
1237
1237
  markdown: r,
1238
- query: o
1238
+ query: a
1239
1239
  };
1240
1240
  this.deps.dashboardStore.updateMultipleItemProperties(this.deps.dashboardId, m, { content: e });
1241
1241
  }
1242
- if (c) {
1242
+ if (s) {
1243
1243
  let e = {
1244
- html: l,
1244
+ html: c,
1245
1245
  query: r
1246
1246
  };
1247
1247
  this.deps.dashboardStore.updateMultipleItemProperties(this.deps.dashboardId, m, { content: e });
1248
1248
  }
1249
- e.chartConfig && i === a.CHART && this.deps.dashboardStore.updateItemChartConfig(this.deps.dashboardId, m, e.chartConfig);
1249
+ e.chartConfig && i === l.CHART && this.deps.dashboardStore.updateItemChartConfig(this.deps.dashboardId, m, e.chartConfig);
1250
1250
  let h;
1251
- (i === a.CHART || i === a.TABLE || c || s) && (h = this.deps.refreshItem(m));
1252
- let g = `Added ${n} item "${f}" (ID: ${m}) at position (0, ${p}, ${u}x${d}).${s ? " Markdown is bound to query results for dynamic data." : ""}${c ? " The widget receives this query’s rows via window.trilogy." : ""}`;
1253
- if (e.chartConfig && i === a.CHART && h) {
1251
+ (i === l.CHART || i === l.TABLE || s || o) && (h = this.deps.refreshItem(m));
1252
+ let g = `Added ${n} item "${f}" (ID: ${m}) at position (0, ${p}, ${u}x${d}).${o ? " Markdown is bound to query results for dynamic data." : ""}${s ? " The widget receives this query’s rows via window.trilogy." : ""}`;
1253
+ if (e.chartConfig && i === l.CHART && h) {
1254
1254
  let t = await this.validateChartConfigAfterQuery(m, h, e.chartConfig);
1255
1255
  if (t) return {
1256
1256
  success: !1,
@@ -1273,9 +1273,9 @@ var ht = class {
1273
1273
  success: !1,
1274
1274
  error: `Item "${n}" not found. Use list_dashboard_items to see available items.`
1275
1275
  };
1276
- let r = [], i = t.gridItems[n], o = e.type || i.type, s = o === a.MARKDOWN;
1277
- if (o === a.FREEFORM && (e.content !== void 0 || e.html !== void 0)) {
1278
- let t = i.content, a = typeof t == "string" ? "" : t?.html || "", o = typeof t == "string" ? t : t?.query || "", s = e.html === void 0 ? a : e.html, c = e.content === void 0 ? o : e.content, l = dt(s);
1276
+ let r = [], i = t.gridItems[n], a = e.type || i.type, o = a === l.MARKDOWN;
1277
+ if (a === l.FREEFORM && (e.content !== void 0 || e.html !== void 0)) {
1278
+ let t = i.content, a = typeof t == "string" ? "" : t?.html || "", o = typeof t == "string" ? t : t?.query || "", s = e.html === void 0 ? a : e.html, c = e.content === void 0 ? o : e.content, l = lt(s);
1279
1279
  if (l) return {
1280
1280
  success: !1,
1281
1281
  error: l
@@ -1284,7 +1284,7 @@ var ht = class {
1284
1284
  html: s,
1285
1285
  query: c
1286
1286
  } }), e.content !== void 0 && r.push("content"), e.html !== void 0 && r.push("html");
1287
- } else if (s && (e.content !== void 0 || e.query !== void 0)) {
1287
+ } else if (o && (e.content !== void 0 || e.query !== void 0)) {
1288
1288
  let t = i.content, a = typeof t == "string" ? t : t?.markdown || "", o = typeof t == "string" ? "" : t?.query || "", s = e.content === void 0 ? a : e.content, c = e.query === void 0 ? o : e.query, l = c ? {
1289
1289
  markdown: s,
1290
1290
  query: c
@@ -1295,25 +1295,25 @@ var ht = class {
1295
1295
  success: !0,
1296
1296
  message: `No updates provided for item "${n}".`
1297
1297
  };
1298
- let c = t.gridItems[n], l, u = c.type === a.CHART || c.type === a.TABLE || c.type === a.FREEFORM || c.type === a.MARKDOWN && typeof c.content == "object" && !!c.content.query;
1299
- (r.includes("content") || r.includes("type") || r.includes("query")) && u && (l = this.deps.refreshItem(n));
1298
+ let s = t.gridItems[n], c, u = s.type === l.CHART || s.type === l.TABLE || s.type === l.FREEFORM || s.type === l.MARKDOWN && typeof s.content == "object" && !!s.content.query;
1299
+ (r.includes("content") || r.includes("type") || r.includes("query")) && u && (c = this.deps.refreshItem(n));
1300
1300
  let d = `Updated item "${n}": ${r.join(", ")}.`;
1301
- if (r.includes("chartConfig") && c.type === a.CHART) {
1302
- if (l) {
1303
- let t = await this.validateChartConfigAfterQuery(n, l, e.chartConfig);
1301
+ if (r.includes("chartConfig") && s.type === l.CHART) {
1302
+ if (c) {
1303
+ let t = await this.validateChartConfigAfterQuery(n, c, e.chartConfig);
1304
1304
  if (t) return {
1305
1305
  success: !1,
1306
1306
  error: `${d}\n\nHowever, the chart configuration is invalid:\n${t}`
1307
1307
  };
1308
- } else if (c.results) {
1309
- let t = c.results;
1308
+ } else if (s.results) {
1309
+ let t = s.results;
1310
1310
  if (t.headers) {
1311
- let r = p(t.data, t.headers, e.chartConfig);
1311
+ let r = v(t.data, t.headers, e.chartConfig);
1312
1312
  if (!r.valid) {
1313
1313
  let e = r.suggestedConfig;
1314
1314
  return e && e.chartType && this.deps.dashboardStore.updateItemChartConfig(this.deps.dashboardId, n, e), {
1315
1315
  success: !1,
1316
- error: `${d}\n\nAuto-corrected to default configuration.\n${b(r)}`
1316
+ error: `${d}\n\nAuto-corrected to default configuration.\n${C(r)}`
1317
1317
  };
1318
1318
  }
1319
1319
  }
@@ -1373,7 +1373,7 @@ var ht = class {
1373
1373
  itemCount: Object.keys(e.gridItems).length,
1374
1374
  imports: e.imports,
1375
1375
  filter: e.filter,
1376
- theme: n(e.theme),
1376
+ theme: x(e.theme),
1377
1377
  createdAt: e.createdAt,
1378
1378
  updatedAt: e.updatedAt
1379
1379
  };
@@ -1397,8 +1397,8 @@ var ht = class {
1397
1397
  };
1398
1398
  }
1399
1399
  setDashboardTheme(e) {
1400
- let t = this.dashboard;
1401
- if (!t) return {
1400
+ let n = this.dashboard;
1401
+ if (!n) return {
1402
1402
  success: !1,
1403
1403
  error: "Dashboard not found."
1404
1404
  };
@@ -1406,20 +1406,20 @@ var ht = class {
1406
1406
  success: !0,
1407
1407
  message: "Dashboard theme cleared. It now inherits the app light/dark styling."
1408
1408
  };
1409
- let r = {}, i = [], a = [
1410
- ["preset", ae],
1411
- ["corners", g],
1412
- ["density", o],
1413
- ["elevation", xe]
1409
+ let i = {}, a = [], o = [
1410
+ ["preset", t],
1411
+ ["corners", r],
1412
+ ["density", ye],
1413
+ ["elevation", g]
1414
1414
  ];
1415
- for (let [t, n] of a) {
1416
- let a = e[t];
1417
- if (a != null) {
1418
- if (!n.includes(a)) return {
1415
+ for (let [t, n] of o) {
1416
+ let r = e[t];
1417
+ if (r != null) {
1418
+ if (!n.includes(r)) return {
1419
1419
  success: !1,
1420
- error: `Invalid ${t} "${a}". Must be one of: ${n.join(", ")}.`
1420
+ error: `Invalid ${t} "${r}". Must be one of: ${n.join(", ")}.`
1421
1421
  };
1422
- r[t] = a, i.push(`${t}=${a}`);
1422
+ i[t] = r, a.push(`${t}=${r}`);
1423
1423
  }
1424
1424
  }
1425
1425
  if (e.mobileCards !== void 0 && e.mobileCards !== null) {
@@ -1427,9 +1427,9 @@ var ht = class {
1427
1427
  success: !1,
1428
1428
  error: "mobileCards must be true or false."
1429
1429
  };
1430
- r.mobileCards = e.mobileCards, i.push(`mobileCards=${e.mobileCards}`);
1430
+ i.mobileCards = e.mobileCards, a.push(`mobileCards=${e.mobileCards}`);
1431
1431
  }
1432
- for (let t of ne) {
1432
+ for (let t of d) {
1433
1433
  let n = e[t.key];
1434
1434
  if (n != null) {
1435
1435
  if (typeof n != "string") return {
@@ -1437,22 +1437,22 @@ var ht = class {
1437
1437
  error: `${t.key} must be a color string.`
1438
1438
  };
1439
1439
  if (n.trim() === "") {
1440
- r[t.key] = void 0, i.push(`${t.key} cleared`);
1440
+ i[t.key] = void 0, a.push(`${t.key} cleared`);
1441
1441
  continue;
1442
1442
  }
1443
- if (!le(n)) return {
1443
+ if (!he(n)) return {
1444
1444
  success: !1,
1445
1445
  error: `Invalid ${t.key} "${n}". Use a hex string ("#101820"), rgb()/rgba()/hsl(), or a CSS color name.`
1446
1446
  };
1447
- r[t.key] = n.trim(), i.push(`${t.key}=${n.trim()}`);
1447
+ i[t.key] = n.trim(), a.push(`${t.key}=${n.trim()}`);
1448
1448
  }
1449
1449
  }
1450
- return i.length === 0 ? {
1450
+ return a.length === 0 ? {
1451
1451
  success: !0,
1452
- message: `No theme changes provided. Current theme: ${n(t.theme)}. Pass reset=true to clear it.`
1453
- } : (this.deps.dashboardStore.setDashboardTheme(this.deps.dashboardId, r), {
1452
+ message: `No theme changes provided. Current theme: ${x(n.theme)}. Pass reset=true to clear it.`
1453
+ } : (this.deps.dashboardStore.setDashboardTheme(this.deps.dashboardId, i), {
1454
1454
  success: !0,
1455
- message: `Theme updated (${i.join(", ")}). Full theme is now: ${n(this.dashboard?.theme)}. Call capture_dashboard_screenshot to confirm it reads well — custom colors override the app palette, so check contrast rather than assuming it.`
1455
+ message: `Theme updated (${a.join(", ")}). Full theme is now: ${x(this.dashboard?.theme)}. Call capture_dashboard_screenshot to confirm it reads well — custom colors override the app palette, so check contrast rather than assuming it.`
1456
1456
  });
1457
1457
  }
1458
1458
  getChartAxisThresholdWarnings() {
@@ -1460,17 +1460,17 @@ var ht = class {
1460
1460
  if (!e) return [];
1461
1461
  let t = [];
1462
1462
  for (let [n, r] of Object.entries(e.gridItems)) {
1463
- if (r.type !== a.CHART || !r.chartConfig || !r.results) continue;
1464
- let i = ft[r.chartConfig.chartType];
1463
+ if (r.type !== l.CHART || !r.chartConfig || !r.results) continue;
1464
+ let i = ut[r.chartConfig.chartType];
1465
1465
  if (!i) continue;
1466
- let o = r.results.data, s = e.layout.find((e) => e.i === n), c = r.name || "(untitled)";
1466
+ let a = r.results.data, o = e.layout.find((e) => e.i === n), s = r.name || "(untitled)";
1467
1467
  for (let e of ["xField", "yField"]) {
1468
- let a = i[e], l = r.chartConfig[e];
1469
- if (!a || !l) continue;
1470
- let u = mt(o, l);
1471
- if (u <= a) continue;
1472
- let d = e === "xField" ? "x-axis" : "y-axis", f = s ? ` Current size: w=${s.w}, h=${s.h}.` : "";
1473
- t.push(`- [${n}] ${r.chartConfig.chartType} "${c}": ${d} field "${l}" has ${u} distinct values, exceeding the recommended limit of ${a}.${f}`);
1468
+ let c = i[e], l = r.chartConfig[e];
1469
+ if (!c || !l) continue;
1470
+ let u = ft(a, l);
1471
+ if (u <= c) continue;
1472
+ let d = e === "xField" ? "x-axis" : "y-axis", f = o ? ` Current size: w=${o.w}, h=${o.h}.` : "";
1473
+ t.push(`- [${n}] ${r.chartConfig.chartType} "${s}": ${d} field "${l}" has ${u} distinct values, exceeding the recommended limit of ${c}.${f}`);
1474
1474
  }
1475
1475
  }
1476
1476
  return t;
@@ -1511,23 +1511,23 @@ var ht = class {
1511
1511
  };
1512
1512
  }
1513
1513
  }
1514
- async runTrilogyQuery(e, n) {
1515
- let r = n || this.connectionName;
1516
- if (!r) return {
1514
+ async runTrilogyQuery(e, t) {
1515
+ let n = t || this.connectionName;
1516
+ if (!n) return {
1517
1517
  success: !1,
1518
1518
  error: "No connection name specified and no dashboard connection set."
1519
1519
  };
1520
- let i = this.deps.getActiveImports(), a = await _(this.deps.queryExecutionService, this.deps.connectionStore, this.deps.editorStore, r, e, i);
1521
- if (!a.success) return a;
1522
- let { queryResult: o } = a, s = typeof o.results.toJSON == "function" ? o.results.toJSON() : o.results, { head: c, totalRows: l, cutCount: u } = t(s), d;
1523
- return u > 0 ? (d = JSON.stringify({
1524
- ...s,
1525
- data: c
1526
- }, null, 2), d += `\n\n(Showing first ${c.length} of ${l} rows)`) : d = JSON.stringify(s, null, 2), {
1520
+ let r = this.deps.getActiveImports(), i = await _(this.deps.queryExecutionService, this.deps.connectionStore, this.deps.editorStore, n, e, r);
1521
+ if (!i.success) return i;
1522
+ let { queryResult: a } = i, o = typeof a.results.toJSON == "function" ? a.results.toJSON() : a.results, { head: s, totalRows: c, cutCount: l } = oe(o), u;
1523
+ return l > 0 ? (u = JSON.stringify({
1524
+ ...o,
1525
+ data: s
1526
+ }, null, 2), u += `\n\n(Showing first ${s.length} of ${c} rows)`) : u = JSON.stringify(o, null, 2), {
1527
1527
  success: !0,
1528
- message: `Query executed successfully (${o.executionTime}ms, ${l} rows):\n${d}`,
1528
+ message: `Query executed successfully (${a.executionTime}ms, ${c} rows):\n${u}`,
1529
1529
  query: e,
1530
- generatedSql: o.generatedSql
1530
+ generatedSql: a.generatedSql
1531
1531
  };
1532
1532
  }
1533
1533
  get importAccessor() {
@@ -1538,22 +1538,22 @@ var ht = class {
1538
1538
  };
1539
1539
  }
1540
1540
  async selectActiveImport(e) {
1541
- return he(e, this.importAccessor, {
1541
+ return de(e, this.importAccessor, {
1542
1542
  connectionStore: this.deps.connectionStore,
1543
1543
  editorStore: this.deps.editorStore,
1544
1544
  queryExecutionService: this.deps.queryExecutionService
1545
1545
  }, this.deps.editorStore, this.deps.connectionStore);
1546
1546
  }
1547
1547
  listAvailableImports() {
1548
- return oe(this.importAccessor, this.deps.editorStore, this.deps.connectionStore);
1548
+ return ie(this.importAccessor, this.deps.editorStore, this.deps.connectionStore);
1549
1549
  }
1550
1550
  async connectDataConnection(e) {
1551
- return de(this.deps.connectionStore, e);
1551
+ return ce(this.deps.connectionStore, e);
1552
1552
  }
1553
1553
  findMemoItemId() {
1554
1554
  let e = this.dashboard;
1555
1555
  if (!e) return null;
1556
- for (let [t, n] of Object.entries(e.gridItems)) if (n.type === a.MEMO) return t;
1556
+ for (let [t, n] of Object.entries(e.gridItems)) if (n.type === l.MEMO) return t;
1557
1557
  return null;
1558
1558
  }
1559
1559
  async setExecutiveMemo(e) {
@@ -1600,13 +1600,13 @@ var ht = class {
1600
1600
  confidence: n,
1601
1601
  confidenceRationale: typeof e.confidence_rationale == "string" ? e.confidence_rationale : void 0,
1602
1602
  query: typeof e.query == "string" && e.query.trim() ? e.query : void 0
1603
- }, i = this.findMemoItemId(), o;
1603
+ }, i = this.findMemoItemId(), a;
1604
1604
  return i ? (this.deps.dashboardStore.updateMultipleItemProperties(this.deps.dashboardId, i, {
1605
1605
  content: r,
1606
1606
  name: r.headline.slice(0, 80) || "Executive Memo"
1607
- }), o = i) : (o = this.deps.dashboardStore.addItemToDashboard(this.deps.dashboardId, a.MEMO, 0, 0, 20, 8, "", r.headline.slice(0, 80) || "Executive Memo"), this.deps.dashboardStore.updateMultipleItemProperties(this.deps.dashboardId, o, { content: r })), r.query && this.deps.refreshItem(o), {
1607
+ }), a = i) : (a = this.deps.dashboardStore.addItemToDashboard(this.deps.dashboardId, l.MEMO, 0, 0, 20, 8, "", r.headline.slice(0, 80) || "Executive Memo"), this.deps.dashboardStore.updateMultipleItemProperties(this.deps.dashboardId, a, { content: r })), r.query && this.deps.refreshItem(a), {
1608
1608
  success: !0,
1609
- message: i ? `Updated executive memo (id ${o}). Verdict: ${t} · confidence: ${n}.` : `Added executive memo (id ${o}). Verdict: ${t} · confidence: ${n}.`
1609
+ message: i ? `Updated executive memo (id ${a}). Verdict: ${t} · confidence: ${n}.` : `Added executive memo (id ${a}). Verdict: ${t} · confidence: ${n}.`
1610
1610
  };
1611
1611
  }
1612
1612
  async addClaimSection(e) {
@@ -1625,7 +1625,7 @@ var ht = class {
1625
1625
  caveat: typeof e.caveat == "string" ? e.caveat : void 0,
1626
1626
  drilldown: typeof e.drilldown == "string" ? e.drilldown : void 0,
1627
1627
  query: typeof e.query == "string" && e.query.trim() ? e.query : void 0
1628
- }, r = n.heading || t.slice(0, 80), i = this.deps.dashboardStore.addItemToDashboard(this.deps.dashboardId, a.CLAIM, 0, 0, 20, 4, "", r);
1628
+ }, r = n.heading || t.slice(0, 80), i = this.deps.dashboardStore.addItemToDashboard(this.deps.dashboardId, l.CLAIM, 0, 0, 20, 4, "", r);
1629
1629
  return this.deps.dashboardStore.updateMultipleItemProperties(this.deps.dashboardId, i, { content: n }), n.query && this.deps.refreshItem(i), {
1630
1630
  success: !0,
1631
1631
  message: `Added claim section (id ${i}). Pair this claim with an evidence chart by calling add_dashboard_item next; the report renderer keeps adjacent blocks visually together.`
@@ -1637,14 +1637,14 @@ var ht = class {
1637
1637
  success: !1,
1638
1638
  error: "Dashboard not found."
1639
1639
  };
1640
- for (let [e, n] of Object.entries(t.gridItems)) if (n.type === a.APPENDIX_HEADER) return {
1640
+ for (let [e, n] of Object.entries(t.gridItems)) if (n.type === l.APPENDIX_HEADER) return {
1641
1641
  success: !1,
1642
1642
  error: `Appendix header already exists (id ${e}). A report should have at most one.`
1643
1643
  };
1644
1644
  let n = typeof e.label == "string" && e.label.trim() ? e.label.trim() : "Appendix";
1645
1645
  return {
1646
1646
  success: !0,
1647
- message: `Added appendix divider (id ${this.deps.dashboardStore.addItemToDashboard(this.deps.dashboardId, a.APPENDIX_HEADER, 0, 0, 20, 1, "", n)}, label "${n}"). Subsequent items added to the report are rendered as appendix content.`
1647
+ message: `Added appendix divider (id ${this.deps.dashboardStore.addItemToDashboard(this.deps.dashboardId, l.APPENDIX_HEADER, 0, 0, 20, 1, "", n)}, label "${n}"). Subsequent items added to the report are rendered as appendix content.`
1648
1648
  };
1649
1649
  }
1650
1650
  setReportLayout(e) {
@@ -1668,7 +1668,7 @@ var ht = class {
1668
1668
  let n = t.gridItems[e];
1669
1669
  return n ? {
1670
1670
  success: !0,
1671
- message: ut(lt(e, n, t))
1671
+ message: ct(st(e, n, t))
1672
1672
  } : {
1673
1673
  success: !1,
1674
1674
  error: `Item "${e}" not found. Use list_dashboard_items to see available items.`
@@ -1677,9 +1677,9 @@ var ht = class {
1677
1677
  };
1678
1678
  //#endregion
1679
1679
  //#region dashboards/itemData.ts
1680
- function A(e) {
1680
+ function N(e) {
1681
1681
  return {
1682
- type: a.CHART,
1682
+ type: l.CHART,
1683
1683
  content: "",
1684
1684
  structured_content: {
1685
1685
  markdown: "",
@@ -1695,28 +1695,28 @@ function A(e) {
1695
1695
  hasDrilldown: !1
1696
1696
  };
1697
1697
  }
1698
- function gt(e, t) {
1698
+ function mt(e, t) {
1699
1699
  return e.imports.map((e) => ({
1700
1700
  alias: e.name,
1701
1701
  contents: t.editors[e.id]?.contents || t.editors[e.name]?.contents || ""
1702
1702
  }));
1703
1703
  }
1704
- function j(e) {
1704
+ function P(e) {
1705
1705
  return e && typeof e == "object" && typeof e.markdown == "string" && typeof e.query == "string";
1706
1706
  }
1707
- function _t(e) {
1707
+ function ht(e) {
1708
1708
  return e && typeof e == "object" && "headline" in e && "verdict" in e;
1709
1709
  }
1710
- function vt(e) {
1710
+ function gt(e) {
1711
1711
  return e && typeof e == "object" && "claim" in e && !("headline" in e);
1712
1712
  }
1713
- function yt(e) {
1713
+ function _t(e) {
1714
1714
  return e && typeof e == "object" && typeof e.html == "string";
1715
1715
  }
1716
- function bt(e, t, n) {
1716
+ function vt(e, t, n) {
1717
1717
  let r = e.gridItems[t];
1718
1718
  if (!r) return {
1719
- ...A(t),
1719
+ ...N(t),
1720
1720
  imports: e.imports,
1721
1721
  connectionName: e.connection || ""
1722
1722
  };
@@ -1725,27 +1725,27 @@ function bt(e, t, n) {
1725
1725
  value: e.filter,
1726
1726
  source: "global"
1727
1727
  }, ...i]));
1728
- let o = !1, s = j(r.content) ? r.content : _t(r.content) || vt(r.content) || yt(r.content) ? {
1728
+ let o = !1, s = P(r.content) ? r.content : ht(r.content) || gt(r.content) || _t(r.content) ? {
1729
1729
  markdown: "",
1730
1730
  query: r.content.query || ""
1731
1731
  } : {
1732
1732
  markdown: r.type === "markdown" ? r.content : "",
1733
1733
  query: r.type === "markdown" ? "" : r.content
1734
1734
  };
1735
- r.drilldown && (o = !0, s = j(r.drilldown) ? r.drilldown : {
1735
+ r.drilldown && (o = !0, s = P(r.drilldown) ? r.drilldown : {
1736
1736
  markdown: r.type === "markdown" ? r.drilldown : "",
1737
1737
  query: r.type === "markdown" ? "" : r.drilldown
1738
1738
  });
1739
1739
  let c = r.chartConfig;
1740
1740
  o && (c = r.drilldownChartConfig || void 0);
1741
1741
  let l = "";
1742
- return typeof r.content == "string" ? l = r.content : j(r.content) && (l = r.content.markdown), {
1742
+ return typeof r.content == "string" ? l = r.content : P(r.content) && (l = r.content.markdown), {
1743
1743
  type: r.type,
1744
1744
  content: l,
1745
1745
  structured_content: s,
1746
- memoData: _t(r.content) ? r.content : void 0,
1747
- claimData: vt(r.content) ? r.content : void 0,
1748
- freeformData: yt(r.content) ? r.content : void 0,
1746
+ memoData: ht(r.content) ? r.content : void 0,
1747
+ claimData: gt(r.content) ? r.content : void 0,
1748
+ freeformData: _t(r.content) ? r.content : void 0,
1749
1749
  name: r.name,
1750
1750
  allowCrossFilter: r.allowCrossFilter !== !1,
1751
1751
  width: r.width || 0,
@@ -1766,7 +1766,7 @@ function bt(e, t, n) {
1766
1766
  hasDrilldown: o
1767
1767
  };
1768
1768
  }
1769
- function xt(e, t, n, r) {
1769
+ function yt(e, t, n, r) {
1770
1770
  let i = {};
1771
1771
  r.name && (i.name = r.name), r.chartConfig && (i.chartConfig = r.chartConfig), r.content && (i.content = r.content), r.dimensions && (i.layoutDimensions = {
1772
1772
  w: r.dimensions.width,
@@ -1775,13 +1775,13 @@ function xt(e, t, n, r) {
1775
1775
  }
1776
1776
  //#endregion
1777
1777
  //#region llm/dashboardAgentRuntime.ts
1778
- function St(e, t, n) {
1778
+ function bt(e, t, n) {
1779
1779
  let r = e.chatId;
1780
1780
  if (r) {
1781
1781
  let e = t.chats[r];
1782
1782
  if (e && !e.deleted) return r;
1783
1783
  }
1784
- let i = new c({
1784
+ let i = new o({
1785
1785
  name: `Dashboard: ${e.name}`,
1786
1786
  llmConnectionName: n.activeConnection || "",
1787
1787
  dataConnectionName: e.connection || "",
@@ -1790,49 +1790,49 @@ function St(e, t, n) {
1790
1790
  });
1791
1791
  return t.addChat(i), e.setChatId(i.id), i.id;
1792
1792
  }
1793
- function Ct(e) {
1794
- return e.layoutType === "report" ? [...qe, ...Je] : qe;
1793
+ function xt(e) {
1794
+ return e.layoutType === "report" ? [...j, ...Ke] : j;
1795
1795
  }
1796
- function M(e) {
1796
+ function F(e) {
1797
1797
  return (e.imports || []).map((e) => ({
1798
1798
  id: e.id,
1799
1799
  name: e.name,
1800
1800
  alias: e.alias || ""
1801
1801
  }));
1802
1802
  }
1803
- function wt(e, t, n) {
1803
+ function St(e, t, n) {
1804
1804
  let r = t ? Object.values(t.connections).map((e) => e.name) : [], i = e.connectionId && t?.connections[e.connectionId] || (e.connection ? t?.connectionByName(e.connection) : void 0);
1805
- return Qe({
1805
+ return Xe({
1806
1806
  dashboard: e,
1807
1807
  availableConnections: r,
1808
- availableImportsForConnection: n && i ? Object.values(n.editors).filter((e) => !e.deleted && T(e.type) && e.connectionId === i.id).sort((e, t) => e.name.localeCompare(t.name)).map((e) => ({
1808
+ availableImportsForConnection: n && i ? Object.values(n.editors).filter((e) => !e.deleted && O(e.type) && e.connectionId === i.id).sort((e, t) => e.name.localeCompare(t.name)).map((e) => ({
1809
1809
  id: e.id,
1810
1810
  name: e.name.replace(/\//g, "."),
1811
1811
  alias: ""
1812
1812
  })) : []
1813
1813
  });
1814
1814
  }
1815
- function Tt(e, t) {
1815
+ function Ct(e, t) {
1816
1816
  let n = e.connectionId && t?.connections[e.connectionId] || (e.connection ? t?.connectionByName(e.connection) : void 0);
1817
- return Ye({
1817
+ return qe({
1818
1818
  dashboard: e,
1819
1819
  dataConnectionName: e.connection,
1820
- activeImports: M(e),
1820
+ activeImports: F(e),
1821
1821
  isDataConnectionActive: n?.connected ?? !1
1822
1822
  });
1823
1823
  }
1824
- function Et(e, t, n) {
1824
+ function wt(e, t, n) {
1825
1825
  let r = null;
1826
1826
  return () => {
1827
1827
  let i = e();
1828
- return i ? (r === null && (r = Tt(i, t)), `${wt(i, t, n)}
1828
+ return i ? (r === null && (r = Ct(i, t)), `${St(i, t, n)}
1829
1829
 
1830
1830
  DASHBOARD STATE AT THE START OF THIS CONVERSATION (snapshot — call get_dashboard_state for current state):
1831
1831
  ${r}`) : "";
1832
1832
  };
1833
1833
  }
1834
- function Dt(e, t) {
1835
- let { dashboardStore: n, connectionStore: r, editorStore: i, queryExecutionService: a } = t, o = n.dashboards[e], s = k(o, r);
1834
+ function Tt(e, t) {
1835
+ let { dashboardStore: n, connectionStore: r, editorStore: i, queryExecutionService: a } = t, o = n.dashboards[e], s = M(o, r);
1836
1836
  return n.getOrCreateQueryExecutor(e, {
1837
1837
  queryExecutionService: a,
1838
1838
  connectionName: s,
@@ -1840,14 +1840,14 @@ function Dt(e, t) {
1840
1840
  getDashboardData: (e) => n.dashboards[e],
1841
1841
  getItemData: (e, t) => {
1842
1842
  let r = n.dashboards[t];
1843
- return r ? bt(r, e, { rootContent: gt(r, i) }) : A(e);
1843
+ return r ? vt(r, e, { rootContent: mt(r, i) }) : N(e);
1844
1844
  },
1845
- setItemData: (e, t, r) => xt(n, t, e, r)
1845
+ setItemData: (e, t, r) => yt(n, t, e, r)
1846
1846
  });
1847
1847
  }
1848
- function Ot(e, t) {
1848
+ function Et(e, t) {
1849
1849
  let { dashboardStore: n, connectionStore: r, editorStore: i, queryExecutionService: a } = t;
1850
- return new ht({
1850
+ return new pt({
1851
1851
  dashboardStore: n,
1852
1852
  connectionStore: r,
1853
1853
  editorStore: i,
@@ -1855,7 +1855,7 @@ function Ot(e, t) {
1855
1855
  dashboardId: e,
1856
1856
  getActiveImports: () => {
1857
1857
  let t = n.dashboards[e];
1858
- return t ? M(t) : [];
1858
+ return t ? F(t) : [];
1859
1859
  },
1860
1860
  setActiveImports: (t) => {
1861
1861
  n.updateDashboardImports(e, t.map((e) => ({
@@ -1864,11 +1864,11 @@ function Ot(e, t) {
1864
1864
  alias: e.alias
1865
1865
  })));
1866
1866
  },
1867
- getDashboardQueryExecutor: () => Dt(e, t),
1867
+ getDashboardQueryExecutor: () => Tt(e, t),
1868
1868
  refreshItem: () => void 0
1869
1869
  });
1870
1870
  }
1871
- var kt = /* @__PURE__ */ new Set([
1871
+ var Dt = /* @__PURE__ */ new Set([
1872
1872
  "add_dashboard_item",
1873
1873
  "update_dashboard_item",
1874
1874
  "remove_dashboard_item",
@@ -1879,23 +1879,23 @@ var kt = /* @__PURE__ */ new Set([
1879
1879
  "add_claim_section",
1880
1880
  "add_appendix_header",
1881
1881
  "set_report_layout"
1882
- ]), N = /* @__PURE__ */ new Set();
1883
- async function At(e) {
1882
+ ]), I = /* @__PURE__ */ new Set();
1883
+ async function Ot(e) {
1884
1884
  let { dashboard: t, chatId: n, chatStore: r, dashboardStore: i, setActiveDashboard: a } = e;
1885
1885
  if (t.parentDashboardId) return t.id;
1886
1886
  let o = `${n}:${t.id}`;
1887
- if (N.has(o)) return t.id;
1887
+ if (I.has(o)) return t.id;
1888
1888
  let s = r.chats[n];
1889
- if (s?.sourceRefId === t.id && s?.messages.some((e) => e.executedToolCalls?.some((e) => kt.has(e.name) && e.result?.success)) || Object.keys(t.gridItems).length === 0) return N.add(o), t.id;
1889
+ if (s?.sourceRefId === t.id && s?.messages.some((e) => e.executedToolCalls?.some((e) => Dt.has(e.name) && e.result?.success)) || Object.keys(t.gridItems).length === 0) return I.add(o), t.id;
1890
1890
  let c = (/* @__PURE__ */ new Date()).toISOString().slice(11, 19).replace(/:/g, "");
1891
1891
  try {
1892
1892
  let e = i.forkDashboard(t.id, `chat-${c}`);
1893
- return s && s.sourceRefId === t.id && (t.setChatId(null), e.setChatId(n), s.sourceRefId = e.id, s.changed = !0), N.add(`${n}:${e.id}`), a?.(e.id), e.id;
1893
+ return s && s.sourceRefId === t.id && (t.setChatId(null), e.setChatId(n), s.sourceRefId = e.id, s.changed = !0), I.add(`${n}:${e.id}`), a?.(e.id), e.id;
1894
1894
  } catch (e) {
1895
1895
  return console.error("Failed to auto-fork dashboard for chat mutation:", e), t.id;
1896
1896
  }
1897
1897
  }
1898
- async function jt(e) {
1898
+ async function kt(e) {
1899
1899
  let { chatStore: t, chatId: n, toolExecutor: r, imports: i } = e, a = t.chats[n];
1900
1900
  if (!a || a.messages.length > 0 || i.length === 0) return;
1901
1901
  let o = i[0], s;
@@ -1921,7 +1921,7 @@ async function jt(e) {
1921
1921
  }, d = {
1922
1922
  toolCallId: c,
1923
1923
  toolName: "select_active_import",
1924
- result: te(s)
1924
+ result: ee(s)
1925
1925
  };
1926
1926
  t.addMessageToChat(n, {
1927
1927
  role: "assistant",
@@ -1935,26 +1935,26 @@ async function jt(e) {
1935
1935
  hidden: !0
1936
1936
  });
1937
1937
  }
1938
- async function Mt(e) {
1938
+ async function At(e) {
1939
1939
  let { dashboardId: t, prompt: n, stores: r, deps: i } = e, a = r.dashboardStore.dashboards[t];
1940
1940
  if (!a) throw Error(`Dashboard ${t} not found`);
1941
- let o = St(a, r.chatStore, r.llmConnectionStore), s = Ot(t, r);
1942
- return await jt({
1941
+ let o = bt(a, r.chatStore, r.llmConnectionStore), s = Et(t, r);
1942
+ return await kt({
1943
1943
  chatStore: r.chatStore,
1944
1944
  chatId: o,
1945
1945
  toolExecutor: s,
1946
- imports: M(a)
1946
+ imports: F(a)
1947
1947
  }), r.chatStore.executeMessage(o, n, i, { overrides: {
1948
- tools: Ct(a),
1948
+ tools: xt(a),
1949
1949
  executeToolCall: (e, t) => s.executeToolCall(e, t),
1950
- buildSystemPrompt: Et(() => r.dashboardStore.dashboards[t], r.connectionStore, r.editorStore)
1950
+ buildSystemPrompt: wt(() => r.dashboardStore.dashboards[t], r.connectionStore, r.editorStore)
1951
1951
  } }).catch((e) => {
1952
1952
  console.error(`Headless dashboard agent run for ${t} failed`, e);
1953
1953
  }), o;
1954
1954
  }
1955
1955
  //#endregion
1956
1956
  //#region llm/overseerToolExecutor.ts
1957
- var Nt = class {
1957
+ var jt = class {
1958
1958
  chatStore;
1959
1959
  projectStore;
1960
1960
  overseerChatId;
@@ -1986,9 +1986,9 @@ var Nt = class {
1986
1986
  }
1987
1987
  }
1988
1988
  spawnSubchat(e, t, n) {
1989
- if (!D.includes(e)) return {
1989
+ if (!Re.includes(e)) return {
1990
1990
  success: !1,
1991
- error: `Invalid kind '${e}'. Must be one of: ${D.join(", ")}`
1991
+ error: `Invalid kind '${e}'. Must be one of: ${Re.join(", ")}`
1992
1992
  };
1993
1993
  if (!t || typeof t != "string") return {
1994
1994
  success: !1,
@@ -2004,7 +2004,7 @@ var Nt = class {
2004
2004
  success: !1,
2005
2005
  error: "Overseer chat not found"
2006
2006
  };
2007
- let a = this.projectStore.projects[r], o = a?.dataConnectionId || i.dataConnectionId, s = i.dataConnectionName, c = this.chatStore.newChat(i.llmConnectionName, s, n || Pt(e, t), o);
2007
+ let a = this.projectStore.projects[r], o = a?.dataConnectionId || i.dataConnectionId, s = i.dataConnectionName, c = this.chatStore.newChat(i.llmConnectionName, s, n || Mt(e, t), o);
2008
2008
  return c.kind = e, c.parentChatId = this.overseerChatId, c.parentProjectId = r, c.changed = !0, this.projectStore.addSubchatToProject(r, c.id), this.chatStore.executeMessage(c.id, t, this.deps).catch((e) => {
2009
2009
  console.error(`Subchat ${c.id} failed`, e);
2010
2010
  }), {
@@ -2097,7 +2097,7 @@ var Nt = class {
2097
2097
  };
2098
2098
  let i = this.chatStore.isChatExecuting(e) ? "still running" : "idle / finished";
2099
2099
  try {
2100
- let n = await Se(r, t, "peek");
2100
+ let n = await le(r, t, "peek");
2101
2101
  return {
2102
2102
  success: !0,
2103
2103
  message: `Peek of ${e} (${t.kind}, ${i}):\n\n${n}`
@@ -2149,7 +2149,7 @@ var Nt = class {
2149
2149
  }
2150
2150
  this.projectStore.addDashboardToProject(i, o.id);
2151
2151
  try {
2152
- Mt({
2152
+ At({
2153
2153
  dashboardId: o.id,
2154
2154
  prompt: r,
2155
2155
  stores: {
@@ -2195,7 +2195,7 @@ var Nt = class {
2195
2195
  for (let e of t.subchatIds) {
2196
2196
  let t = this.chatStore.chats[e];
2197
2197
  if (!t) continue;
2198
- let n = this.chatStore.isChatExecuting(e) ? "running" : "idle", i = t.messages[t.messages.length - 1], a = i ? Ft(i.content || "", 80) : "";
2198
+ let n = this.chatStore.isChatExecuting(e) ? "running" : "idle", i = t.messages[t.messages.length - 1], a = i ? Nt(i.content || "", 80) : "";
2199
2199
  r.push(` - ${e} [${t.kind}] (${n}) "${t.name}" — last: ${a}`);
2200
2200
  }
2201
2201
  return {
@@ -2204,15 +2204,15 @@ var Nt = class {
2204
2204
  };
2205
2205
  }
2206
2206
  };
2207
- function Pt(e, t) {
2208
- return `${e}: ${Ft(t.split(/\s+/).slice(0, 6).join(" "), 50)}`;
2207
+ function Mt(e, t) {
2208
+ return `${e}: ${Nt(t.split(/\s+/).slice(0, 6).join(" "), 50)}`;
2209
2209
  }
2210
- function Ft(e, t) {
2210
+ function Nt(e, t) {
2211
2211
  return e.length <= t ? e : e.slice(0, t - 1) + "…";
2212
2212
  }
2213
2213
  //#endregion
2214
2214
  //#region llm/architectToolExecutor.ts
2215
- var It = class {
2215
+ var Pt = class {
2216
2216
  chatStore;
2217
2217
  projectStore;
2218
2218
  editorStore;
@@ -2286,7 +2286,7 @@ var It = class {
2286
2286
  success: !1,
2287
2287
  error: `File "${e}" not found in project.`
2288
2288
  };
2289
- let n = Bt(t.contents);
2289
+ let n = Rt(t.contents);
2290
2290
  return {
2291
2291
  success: !0,
2292
2292
  message: `Contents of ${e} (${t.type}, ${t.contents.length} bytes):\n\n${n}`
@@ -2301,7 +2301,7 @@ var It = class {
2301
2301
  success: !1,
2302
2302
  error: "content is required"
2303
2303
  };
2304
- let r = Rt(t);
2304
+ let r = It(t);
2305
2305
  if (!r) return {
2306
2306
  success: !1,
2307
2307
  error: `type must be 'trilogy' or 'sql' (got ${JSON.stringify(t)}).`
@@ -2311,7 +2311,7 @@ var It = class {
2311
2311
  success: !1,
2312
2312
  error: "No project context."
2313
2313
  };
2314
- let a = zt(e, r);
2314
+ let a = Lt(e, r);
2315
2315
  if (this.findEditorByName(a)) return {
2316
2316
  success: !1,
2317
2317
  error: `File "${a}" already exists. Use update_file to modify.`
@@ -2337,14 +2337,14 @@ var It = class {
2337
2337
  success: !1,
2338
2338
  error: `File "${e}" not found. Use create_file to add it.`
2339
2339
  };
2340
- let r = w(e) || n.type;
2340
+ let r = D(e) || n.type;
2341
2341
  if (r !== "preql" && r !== "trilogy" && r !== "sql") return {
2342
2342
  success: !1,
2343
2343
  error: `update_file handles .preql and .sql files; ${e} is ${r}.`
2344
2344
  };
2345
2345
  n.contents = t, n.changed = !0;
2346
2346
  let i = r === "preql" || r === "trilogy", a = i ? `\n${await this.validateContent(e, t)}` : "", o = "";
2347
- if (n.tags.includes(E.STARTUP_SCRIPT)) {
2347
+ if (n.tags.includes(k.STARTUP_SCRIPT)) {
2348
2348
  let e = this.connection;
2349
2349
  if (e) try {
2350
2350
  await this.connectionStore.connectConnection(e.id), o = `\nRe-ran startup against ${e.name}.`;
@@ -2373,11 +2373,11 @@ var It = class {
2373
2373
  success: !1,
2374
2374
  error: `File "${e}" not found in project.`
2375
2375
  };
2376
- if (n === "none") return r.removeTag(E.STARTUP_SCRIPT), {
2376
+ if (n === "none") return r.removeTag(k.STARTUP_SCRIPT), {
2377
2377
  success: !0,
2378
2378
  message: `Cleared purpose tags on ${e}.`
2379
2379
  };
2380
- r.addTag(E.STARTUP_SCRIPT);
2380
+ r.addTag(k.STARTUP_SCRIPT);
2381
2381
  let i = this.connection;
2382
2382
  if (!i) return {
2383
2383
  success: !0,
@@ -2480,7 +2480,7 @@ var It = class {
2480
2480
  let r = n.queryResult;
2481
2481
  return {
2482
2482
  success: !0,
2483
- message: Vt(r),
2483
+ message: zt(r),
2484
2484
  executionTime: r.executionTime,
2485
2485
  generatedSql: r.generatedSql,
2486
2486
  query: e
@@ -2493,7 +2493,7 @@ var It = class {
2493
2493
  text: "select 1;",
2494
2494
  editorType: "trilogy",
2495
2495
  imports: [],
2496
- extraContent: [...y(this.connectionStore, this.editorStore, n.id), {
2496
+ extraContent: [...b(this.connectionStore, this.editorStore, n.id), {
2497
2497
  alias: e.replace(/\.preql$/, ""),
2498
2498
  contents: t
2499
2499
  }],
@@ -2517,8 +2517,8 @@ var It = class {
2517
2517
  return `Validation threw: ${e instanceof Error ? e.message : String(e)}`;
2518
2518
  }
2519
2519
  }
2520
- }, P = 4e3, Lt = P * 2;
2521
- function Rt(e) {
2520
+ }, L = 4e3, Ft = L * 2;
2521
+ function It(e) {
2522
2522
  switch (typeof e == "string" ? e.toLowerCase() : "") {
2523
2523
  case "trilogy":
2524
2524
  case "preql": return "preql";
@@ -2526,15 +2526,15 @@ function Rt(e) {
2526
2526
  default: return null;
2527
2527
  }
2528
2528
  }
2529
- function zt(e, t) {
2529
+ function Lt(e, t) {
2530
2530
  return t === "preql" ? e.endsWith(".preql") || e.endsWith(".trilogy") ? e : `${e}.preql` : e.endsWith(".sql") ? e : `${e}.sql`;
2531
2531
  }
2532
- function Bt(e) {
2533
- if (e.length <= Lt) return e;
2534
- let t = e.slice(0, P), n = e.slice(-4e3);
2535
- return `${t}\n<redacted ${e.length - P * 2} chars>\n${n}`;
2532
+ function Rt(e) {
2533
+ if (e.length <= Ft) return e;
2534
+ let t = e.slice(0, L), n = e.slice(-4e3);
2535
+ return `${t}\n<redacted ${e.length - L * 2} chars>\n${n}`;
2536
2536
  }
2537
- function Vt(e) {
2537
+ function zt(e) {
2538
2538
  let t = e.generatedSql ? `\nGenerated SQL:\n${e.generatedSql}\n` : "", n = e.results?.data || [], r = e.results?.headers ? Array.from(e.results.headers.keys()) : [], i = n.slice(0, 10);
2539
2539
  return [
2540
2540
  `Query OK — ${n.length} rows in ${e.executionTime}ms.`,
@@ -2545,7 +2545,7 @@ function Vt(e) {
2545
2545
  }
2546
2546
  //#endregion
2547
2547
  //#region llm/registry/toolRegistry.ts
2548
- var Ht = {
2548
+ var Bt = {
2549
2549
  chat: [
2550
2550
  "data",
2551
2551
  "artifacts",
@@ -2580,7 +2580,7 @@ var Ht = {
2580
2580
  "docs",
2581
2581
  "base"
2582
2582
  ]
2583
- }, Ut = class {
2583
+ }, Vt = class {
2584
2584
  tools = /* @__PURE__ */ new Map();
2585
2585
  toolsetCache = /* @__PURE__ */ new Map();
2586
2586
  register(e) {
@@ -2591,7 +2591,7 @@ var Ht = {
2591
2591
  e.forEach((e) => this.register(e));
2592
2592
  }
2593
2593
  toolsForContext(e) {
2594
- let t = Ht[e];
2594
+ let t = Bt[e];
2595
2595
  if (!t) throw Error(`Unknown toolset context '${e}'`);
2596
2596
  let n = new Set(t);
2597
2597
  return [...this.tools.values()].filter((e) => n.has(e.pack));
@@ -2604,7 +2604,7 @@ var Ht = {
2604
2604
  return this.toolsForContext(e).map((e) => e.definition.name);
2605
2605
  }
2606
2606
  createExecutor(e, t, n) {
2607
- let r = new Set(Ht[e]), i = {
2607
+ let r = new Set(Bt[e]), i = {
2608
2608
  runtime: t,
2609
2609
  session: n
2610
2610
  };
@@ -2631,7 +2631,7 @@ var Ht = {
2631
2631
  }
2632
2632
  } };
2633
2633
  }
2634
- }, Wt = class {
2634
+ }, Ht = class {
2635
2635
  queryExecutionService;
2636
2636
  connectionStore;
2637
2637
  chatStore;
@@ -2678,11 +2678,11 @@ var Ht = {
2678
2678
  if (r && n === "chart") {
2679
2679
  let e = o.results;
2680
2680
  if (e && e.headers) {
2681
- let t = p(e.data, e.headers, r);
2682
- t.valid || (s = t.suggestedConfig, c = `Auto-corrected to default configuration.\n${b(t)}`);
2681
+ let t = v(e.data, e.headers, r);
2682
+ t.valid || (s = t.suggestedConfig, c = `Auto-corrected to default configuration.\n${C(t)}`);
2683
2683
  }
2684
2684
  }
2685
- let l = h(), u = {
2685
+ let l = w(), u = {
2686
2686
  id: l,
2687
2687
  type: n,
2688
2688
  data: o.results,
@@ -2726,7 +2726,7 @@ var Ht = {
2726
2726
  let r = this.resolvedChat?.imports || [], i = r.map((e) => ({
2727
2727
  name: e.name,
2728
2728
  alias: e.alias || ""
2729
- })), a = y(this.connectionStore, this.editorStore, t, r), o = {
2729
+ })), a = b(this.connectionStore, this.editorStore, t, r), o = {
2730
2730
  text: e.trim(),
2731
2731
  editorType: "trilogy",
2732
2732
  imports: i,
@@ -2769,20 +2769,20 @@ var Ht = {
2769
2769
  return !this.resolvedChatId || !this.editorStore ? {
2770
2770
  success: !1,
2771
2771
  error: "No active chat or editor store not available"
2772
- } : he(e, this.importAccessor, {
2772
+ } : de(e, this.importAccessor, {
2773
2773
  connectionStore: this.connectionStore,
2774
2774
  editorStore: this.editorStore,
2775
2775
  queryExecutionService: this.queryExecutionService
2776
2776
  }, this.editorStore, this.connectionStore);
2777
2777
  }
2778
2778
  listAvailableImports() {
2779
- return oe(this.importAccessor, this.editorStore, this.connectionStore);
2779
+ return ie(this.importAccessor, this.editorStore, this.connectionStore);
2780
2780
  }
2781
2781
  getAvailableImports(e) {
2782
- return Ce(this.editorStore, e, this.connectionStore);
2782
+ return be(this.editorStore, e, this.connectionStore);
2783
2783
  }
2784
2784
  async connectDataConnection(e) {
2785
- return de(this.connectionStore, e, { onConnected: (e) => {
2785
+ return ce(this.connectionStore, e, { onConnected: (e) => {
2786
2786
  if (this.resolvedChatId && this.chatStore) {
2787
2787
  let t = this.connectionStore.connectionByName(e);
2788
2788
  this.chatStore.updateChatDataConnection(this.resolvedChatId, e, t?.id || "");
@@ -2794,7 +2794,7 @@ var Ht = {
2794
2794
  success: !1,
2795
2795
  error: "Markdown content is required and must be a non-empty string"
2796
2796
  };
2797
- let i = h(), a = null;
2797
+ let i = w(), a = null;
2798
2798
  if (n && n.trim()) {
2799
2799
  if (r ||= this.resolvedChat?.dataConnectionName || "", !r) return {
2800
2800
  success: !1,
@@ -2848,31 +2848,31 @@ var Ht = {
2848
2848
  };
2849
2849
  }
2850
2850
  getArtifact(e) {
2851
- let n = this.resolvedChat;
2852
- if (!n) return {
2851
+ let t = this.resolvedChat;
2852
+ if (!t) return {
2853
2853
  success: !1,
2854
2854
  error: "No active chat session."
2855
2855
  };
2856
- let r = n.getArtifactById(e);
2857
- if (!r) return {
2856
+ let n = t.getArtifactById(e);
2857
+ if (!n) return {
2858
2858
  success: !1,
2859
2859
  error: `Artifact "${e}" not found. Use list_artifacts to see available artifacts.`
2860
2860
  };
2861
- let i = {
2862
- id: r.id,
2863
- type: r.type
2861
+ let r = {
2862
+ id: n.id,
2863
+ type: n.type
2864
2864
  };
2865
- if (r.config && (i.config = { ...r.config }), r.type === "markdown") i.markdown = r.data?.markdown || "", i.query = r.data?.query || "", i.hasQueryResults = !!r.data?.queryResults;
2866
- else if (r.data) {
2867
- let e = typeof r.data.toJSON == "function" ? r.data.toJSON() : r.data, { head: n, tail: a, totalRows: o, cutCount: s } = t(e);
2868
- s > 0 ? (i.data = {
2865
+ if (n.config && (r.config = { ...n.config }), n.type === "markdown") r.markdown = n.data?.markdown || "", r.query = n.data?.query || "", r.hasQueryResults = !!n.data?.queryResults;
2866
+ else if (n.data) {
2867
+ let e = typeof n.data.toJSON == "function" ? n.data.toJSON() : n.data, { head: t, tail: i, totalRows: a, cutCount: o } = oe(e);
2868
+ o > 0 ? (r.data = {
2869
2869
  ...e,
2870
- data: n
2871
- }, i._truncated = `${s} of ${o} rows cut off (showing first 50 and last 50). Use get_artifact_rows to fetch any range.`, i._tail = a) : i.data = e;
2870
+ data: t
2871
+ }, r._truncated = `${o} of ${a} rows cut off (showing first 50 and last 50). Use get_artifact_rows to fetch any range.`, r._tail = i) : r.data = e;
2872
2872
  }
2873
2873
  return {
2874
2874
  success: !0,
2875
- message: `Artifact "${e}" (${r.type}):\n${JSON.stringify(i, null, 2)}`
2875
+ message: `Artifact "${e}" (${n.type}):\n${JSON.stringify(r, null, 2)}`
2876
2876
  };
2877
2877
  }
2878
2878
  getArtifactRows(e, t, n) {
@@ -2885,7 +2885,7 @@ var Ht = {
2885
2885
  return i ? i.type !== "results" && i.type !== "chart" ? {
2886
2886
  success: !1,
2887
2887
  error: `Artifact "${e}" does not contain tabular data (type: ${i.type}). Only results and chart artifacts support row fetching.`
2888
- } : ve(e, i.data, t, n) : {
2888
+ } : ge(e, i.data, t, n) : {
2889
2889
  success: !1,
2890
2890
  error: `Artifact "${e}" not found. Use list_artifacts to see available artifacts.`
2891
2891
  };
@@ -2911,7 +2911,7 @@ var Ht = {
2911
2911
  }, o.push("title")), r !== void 0 && (a.type === "chart" || a.type === "results")) {
2912
2912
  let t = a.data;
2913
2913
  if (t && t.headers) {
2914
- let n = p(t.data, t.headers, r);
2914
+ let n = v(t.data, t.headers, r);
2915
2915
  if (!n.valid) {
2916
2916
  let t = n.suggestedConfig;
2917
2917
  return a.config = {
@@ -2919,7 +2919,7 @@ var Ht = {
2919
2919
  chartConfig: t
2920
2920
  }, i.updatedAt = /* @__PURE__ */ new Date(), i.changed = !0, {
2921
2921
  success: !1,
2922
- error: `Auto-corrected artifact "${e}" chart config to default configuration.\n` + b(n)
2922
+ error: `Auto-corrected artifact "${e}" chart config to default configuration.\n` + C(n)
2923
2923
  };
2924
2924
  }
2925
2925
  }
@@ -2983,13 +2983,13 @@ var Ht = {
2983
2983
  message: r.length > 0 ? `Hidden ${i.length} artifact(s). Not found: ${r.join(", ")}. Hidden artifacts remain accessible to you via list_artifacts and can be restored by the user.` : `Hidden ${i.length} artifact(s). They remain accessible via list_artifacts and can be restored by the user.`
2984
2984
  };
2985
2985
  }
2986
- }, Gt = [
2986
+ }, Ut = [
2987
2987
  "run_trilogy_query",
2988
2988
  "chart_trilogy_query",
2989
2989
  "select_active_import",
2990
2990
  "list_available_imports",
2991
2991
  "connect_data_connection"
2992
- ], Kt = [
2992
+ ], Wt = [
2993
2993
  "create_markdown",
2994
2994
  "list_artifacts",
2995
2995
  "get_artifact",
@@ -2997,35 +2997,35 @@ var Ht = {
2997
2997
  "update_artifact",
2998
2998
  "hide_artifact",
2999
2999
  "reorder_artifacts"
3000
- ], qt = ["return_to_user"];
3001
- function Jt(e) {
3002
- let t = fe.find((t) => t.name === e);
3000
+ ], Gt = ["return_to_user"];
3001
+ function Kt(e) {
3002
+ let t = ne.find((t) => t.name === e);
3003
3003
  if (!t) throw Error(`CHAT_TOOLS is missing expected tool '${e}'`);
3004
3004
  return t;
3005
3005
  }
3006
- function Yt(e) {
3006
+ function qt(e) {
3007
3007
  let t = "chatToolExecutor", n = e.session.cache.get(t);
3008
- return n || (n = new Wt(e.runtime.queryExecutionService, e.runtime.connectionStore, e.runtime.chatStore, e.runtime.editorStore, e.session.chatId), e.session.cache.set(t, n)), n;
3008
+ return n || (n = new Ht(e.runtime.queryExecutionService, e.runtime.connectionStore, e.runtime.chatStore, e.runtime.editorStore, e.session.chatId), e.session.cache.set(t, n)), n;
3009
3009
  }
3010
- function F(e, t) {
3010
+ function R(e, t) {
3011
3011
  return e.map((e) => ({
3012
- definition: Jt(e),
3012
+ definition: Kt(e),
3013
3013
  pack: t,
3014
- execute: (t, n) => Yt(n).executeToolCall(e, t)
3014
+ execute: (t, n) => qt(n).executeToolCall(e, t)
3015
3015
  }));
3016
3016
  }
3017
- function Xt() {
3018
- return F(Gt, "data");
3017
+ function Jt() {
3018
+ return R(Ut, "data");
3019
3019
  }
3020
- function Zt() {
3021
- return F(Kt, "artifacts");
3020
+ function Yt() {
3021
+ return R(Wt, "artifacts");
3022
3022
  }
3023
- function Qt() {
3024
- return F(qt, "base");
3023
+ function Xt() {
3024
+ return R(Gt, "base");
3025
3025
  }
3026
3026
  //#endregion
3027
3027
  //#region llm/registry/packs/navigationPack.ts
3028
- var I = [
3028
+ var z = [
3029
3029
  "editors",
3030
3030
  "connections",
3031
3031
  "dashboard",
@@ -3036,25 +3036,25 @@ var I = [
3036
3036
  "welcome",
3037
3037
  "settings"
3038
3038
  ];
3039
- function L(e) {
3039
+ function B(e) {
3040
3040
  return e.runtime.navigation ? { available: !0 } : {
3041
3041
  available: !1,
3042
3042
  hint: "App navigation is not available in this embedding context."
3043
3043
  };
3044
3044
  }
3045
- function $t(e, t) {
3045
+ function Zt(e, t) {
3046
3046
  let n = e.runtime.dashboardStore;
3047
3047
  if (!n) return null;
3048
3048
  let r = Object.prototype.hasOwnProperty.call(n.dashboards, t) ? n.dashboards[t] : void 0;
3049
3049
  return r && !r.deleted ? r : Object.values(n.dashboards).find((e) => e.name === t && !e.deleted) || null;
3050
3050
  }
3051
- function en(e, t) {
3051
+ function Qt(e, t) {
3052
3052
  let n = e.runtime.editorStore, r = Object.prototype.hasOwnProperty.call(n.editors, t) ? n.editors[t] : void 0;
3053
3053
  if (r && !r.deleted) return r;
3054
3054
  let i = n.getEditorByName(t);
3055
3055
  return i && !i.deleted ? i : null;
3056
3056
  }
3057
- async function tn(e) {
3057
+ async function $t(e) {
3058
3058
  let t = e.runtime.navigation, n = [];
3059
3059
  n.push(`Active screen: ${t.activeScreen.value || "welcome"}`);
3060
3060
  let r = t.tabs.value;
@@ -3079,11 +3079,11 @@ async function tn(e) {
3079
3079
  message: n.join("\n")
3080
3080
  };
3081
3081
  }
3082
- function nn() {
3082
+ function en() {
3083
3083
  return [
3084
3084
  {
3085
3085
  pack: "navigation",
3086
- availability: L,
3086
+ availability: B,
3087
3087
  definition: {
3088
3088
  name: "get_app_state",
3089
3089
  description: "Get a snapshot of the current app state: which screen the user is viewing, open tabs, the active dashboard/editor, data connections and their status, and available dashboards. Call this to orient yourself before navigating or when context may have changed.",
@@ -3092,11 +3092,11 @@ function nn() {
3092
3092
  properties: {}
3093
3093
  }
3094
3094
  },
3095
- execute: (e, t) => tn(t)
3095
+ execute: (e, t) => $t(t)
3096
3096
  },
3097
3097
  {
3098
3098
  pack: "navigation",
3099
- availability: L,
3099
+ availability: B,
3100
3100
  definition: {
3101
3101
  name: "navigate_to_screen",
3102
3102
  description: "Navigate the app to a different screen. Use open_dashboard/open_editor instead when targeting a specific dashboard or editor.",
@@ -3104,7 +3104,7 @@ function nn() {
3104
3104
  type: "object",
3105
3105
  properties: { screen: {
3106
3106
  type: "string",
3107
- enum: [...I],
3107
+ enum: [...z],
3108
3108
  description: "The screen to navigate to"
3109
3109
  } },
3110
3110
  required: ["screen"]
@@ -3112,9 +3112,9 @@ function nn() {
3112
3112
  },
3113
3113
  execute: async (e, t) => {
3114
3114
  let n = e.screen;
3115
- if (!I.includes(n)) return {
3115
+ if (!z.includes(n)) return {
3116
3116
  success: !1,
3117
- error: `Unknown screen "${e.screen}". Valid screens: ${I.join(", ")}`
3117
+ error: `Unknown screen "${e.screen}". Valid screens: ${z.join(", ")}`
3118
3118
  };
3119
3119
  let r = t.runtime.navigation;
3120
3120
  return r.setActiveScreen(n), r.setActiveSidebarScreen(n), {
@@ -3125,7 +3125,7 @@ function nn() {
3125
3125
  },
3126
3126
  {
3127
3127
  pack: "navigation",
3128
- availability: L,
3128
+ availability: B,
3129
3129
  definition: {
3130
3130
  name: "open_dashboard",
3131
3131
  description: "Open a dashboard in the main pane by id or name. The user will see the dashboard; subsequent dashboard tools default to it.",
@@ -3143,7 +3143,7 @@ function nn() {
3143
3143
  success: !1,
3144
3144
  error: "Dashboard store is not available in this context."
3145
3145
  };
3146
- let n = $t(t, String(e.dashboard_ref ?? ""));
3146
+ let n = Zt(t, String(e.dashboard_ref ?? ""));
3147
3147
  if (!n) {
3148
3148
  let n = Object.values(t.runtime.dashboardStore.dashboards).filter((e) => !e.deleted).map((e) => `"${e.name}"`).join(", ");
3149
3149
  return {
@@ -3159,7 +3159,7 @@ function nn() {
3159
3159
  },
3160
3160
  {
3161
3161
  pack: "navigation",
3162
- availability: L,
3162
+ availability: B,
3163
3163
  definition: {
3164
3164
  name: "open_editor",
3165
3165
  description: "Open an editor (query/model file) in the main pane by id or name, so the user can see it.",
@@ -3173,7 +3173,7 @@ function nn() {
3173
3173
  }
3174
3174
  },
3175
3175
  execute: async (e, t) => {
3176
- let n = en(t, String(e.editor_ref ?? ""));
3176
+ let n = Qt(t, String(e.editor_ref ?? ""));
3177
3177
  return n ? (t.runtime.navigation.setActiveEditor(n.id), {
3178
3178
  success: !0,
3179
3179
  message: `Opened editor "${n.name}" (id ${n.id}, type ${n.type}).`
@@ -3187,29 +3187,29 @@ function nn() {
3187
3187
  }
3188
3188
  //#endregion
3189
3189
  //#region llm/registry/packs/editorPack.ts
3190
- var R = 4e4;
3191
- function z(e, t) {
3190
+ var tn = 4e4;
3191
+ function V(e, t) {
3192
3192
  let n = e.runtime.editorStore, r = Object.prototype.hasOwnProperty.call(n.editors, t) ? n.editors[t] : void 0;
3193
3193
  if (r && !r.deleted) return r;
3194
3194
  let i = n.getEditorByName(t);
3195
3195
  return i && !i.deleted ? i : null;
3196
3196
  }
3197
- function rn(e) {
3197
+ function nn(e) {
3198
3198
  let t = (e.contents || "").split("\n").length;
3199
3199
  return `- "${e.name}" (id ${e.id}, type ${e.type}, connection ${e.connection}, ${t} lines${e.tags.length ? `, tags: ${e.tags.join(",")}` : ""})`;
3200
3200
  }
3201
- function an(e, t) {
3202
- return e.storage === "remote" ? C(t, e.type) : t;
3201
+ function rn(e, t) {
3202
+ return e.storage === "remote" ? E(t, e.type) : t;
3203
3203
  }
3204
- function on(e, t, n) {
3204
+ function an(e, t, n) {
3205
3205
  return Object.values(e.runtime.editorStore.editors).find((e) => !e.deleted && e.id !== t.id && e.connectionId === t.connectionId && e.name === n) || null;
3206
3206
  }
3207
- function sn(e, t, n, r) {
3207
+ function on(e, t, n, r) {
3208
3208
  let i = 0;
3209
3209
  for (let a of Object.values(e.runtime.modelStore?.models || {})) a.sources.some((e) => e.editor === t) && (n === "rename" ? a.updateModelSourceName(t, r || "") : a.removeModelSourceSimple(t), i += 1);
3210
3210
  return i;
3211
3211
  }
3212
- async function B(e, t = !1) {
3212
+ async function H(e, t = !1) {
3213
3213
  let n = [e.runtime.saveEditors, t ? e.runtime.saveModels : void 0].filter((e) => !!e), r = !e.runtime.saveEditors || t && !e.runtime.saveModels;
3214
3214
  try {
3215
3215
  await Promise.all(n.map((e) => Promise.resolve(e())));
@@ -3224,10 +3224,10 @@ async function B(e, t = !1) {
3224
3224
  suffix: r ? " The change is marked for autosave." : " The change was saved."
3225
3225
  };
3226
3226
  }
3227
- function cn(e, t) {
3227
+ function sn(e, t) {
3228
3228
  return e > 0 ? ` The editor source was ${t} in ${e} model${e === 1 ? "" : "s"}.` : "";
3229
3229
  }
3230
- async function ln(e, t) {
3230
+ async function cn(e, t) {
3231
3231
  let n = e.runtime.connectionStore, r = n.connections[t.connectionId] || n.connectionByName(t.connection);
3232
3232
  if (!r) return {
3233
3233
  success: !1,
@@ -3246,7 +3246,7 @@ async function ln(e, t) {
3246
3246
  editorType: t.type === "sql" ? "sql" : "trilogy",
3247
3247
  imports: [],
3248
3248
  currentFilename: t.name,
3249
- extraContent: y(n, e.runtime.editorStore, r.id)
3249
+ extraContent: b(n, e.runtime.editorStore, r.id)
3250
3250
  };
3251
3251
  try {
3252
3252
  let n = await (await e.runtime.queryExecutionService.executeQuery(r.id, i)).resultPromise;
@@ -3262,7 +3262,7 @@ async function ln(e, t) {
3262
3262
  };
3263
3263
  t.chartConfig && t.results?.headers && t.results.headers.size > 0 && JSON.stringify(Array.from(t.results.headers.keys())) !== JSON.stringify(Array.from(n.results.headers.keys())) && t.setChartConfig(null), e.runtime.editorStore.setEditorResults(t.id, n.results), t.executed_contents = t.contents, n.generatedSql && (t.generated_sql = n.generatedSql), typeof n.executionTime == "number" && (t.duration = n.executionTime);
3264
3264
  let a = {
3265
- id: h(),
3265
+ id: w(),
3266
3266
  type: "results",
3267
3267
  data: n.results,
3268
3268
  config: {
@@ -3289,7 +3289,7 @@ async function ln(e, t) {
3289
3289
  };
3290
3290
  }
3291
3291
  }
3292
- function un() {
3292
+ function ln() {
3293
3293
  return [
3294
3294
  {
3295
3295
  pack: "editor",
@@ -3315,7 +3315,7 @@ function un() {
3315
3315
  message: e.connection ? `No editors found for connection "${e.connection}".` : "No editors exist yet. Use create_editor to make one."
3316
3316
  } : {
3317
3317
  success: !0,
3318
- message: `${n.length} editor(s):\n${n.sort((e, t) => e.name.localeCompare(t.name)).map(rn).join("\n")}`
3318
+ message: `${n.length} editor(s):\n${n.sort((e, t) => e.name.localeCompare(t.name)).map(nn).join("\n")}`
3319
3319
  };
3320
3320
  }
3321
3321
  },
@@ -3334,12 +3334,12 @@ function un() {
3334
3334
  }
3335
3335
  },
3336
3336
  execute: async (e, t) => {
3337
- let n = z(t, String(e.editor_ref ?? ""));
3337
+ let n = V(t, String(e.editor_ref ?? ""));
3338
3338
  if (!n) return {
3339
3339
  success: !1,
3340
3340
  error: `Editor "${e.editor_ref}" not found. Use list_editors to see available editors.`
3341
3341
  };
3342
- let r = n.contents || "", i = r.split("\n").length, a = r.length > R ? `${r.slice(0, R)}\n...(truncated at ${R} of ${r.length} characters)` : r;
3342
+ let r = n.contents || "", i = r.split("\n").length, a = r.length > tn ? `${r.slice(0, tn)}\n...(truncated at ${tn} of ${r.length} characters)` : r;
3343
3343
  return {
3344
3344
  success: !0,
3345
3345
  message: `Editor "${n.name}" (id ${n.id}, type ${n.type}, ${i} lines):\n\n${a}`
@@ -3397,7 +3397,7 @@ function un() {
3397
3397
  };
3398
3398
  let a = e.type === "sql" ? "sql" : "preql", o = t.runtime.editorStore.newEditor(n, a, r.name, e.contents || "");
3399
3399
  t.runtime.navigation?.setActiveEditor(o.id);
3400
- let s = await B(t);
3400
+ let s = await H(t);
3401
3401
  return s.success ? {
3402
3402
  success: !0,
3403
3403
  message: `Created editor "${o.name}" (id ${o.id}, type ${o.type}) on connection "${r.name}".${s.suffix}`
@@ -3425,7 +3425,7 @@ function un() {
3425
3425
  }
3426
3426
  },
3427
3427
  execute: async (e, t) => {
3428
- let n = z(t, String(e.editor_ref ?? ""));
3428
+ let n = V(t, String(e.editor_ref ?? ""));
3429
3429
  if (!n) return {
3430
3430
  success: !1,
3431
3431
  error: `Editor "${e.editor_ref}" not found. Use list_editors to see available editors.`
@@ -3435,19 +3435,19 @@ function un() {
3435
3435
  success: !1,
3436
3436
  error: "New editor name cannot be empty."
3437
3437
  };
3438
- let i = an(n, r), a = on(t, n, i);
3438
+ let i = rn(n, r), a = an(t, n, i);
3439
3439
  if (a) return {
3440
3440
  success: !1,
3441
3441
  error: `An editor named "${i}" already exists on connection "${n.connection}" (id ${a.id}).`
3442
3442
  };
3443
3443
  let o = n.name;
3444
3444
  t.runtime.editorStore.updateEditorName(n.id, r);
3445
- let s = sn(t, n.id, "rename", n.name);
3445
+ let s = on(t, n.id, "rename", n.name);
3446
3446
  t.runtime.navigation?.updateTabName("editors", null, n.id);
3447
- let c = await B(t, s > 0);
3447
+ let c = await H(t, s > 0);
3448
3448
  return c.success ? {
3449
3449
  success: !0,
3450
- message: `Renamed editor "${o}" to "${n.name}" (id ${n.id}).${cn(s, "renamed")}${c.suffix}`
3450
+ message: `Renamed editor "${o}" to "${n.name}" (id ${n.id}).${sn(s, "renamed")}${c.suffix}`
3451
3451
  } : c;
3452
3452
  }
3453
3453
  },
@@ -3472,7 +3472,7 @@ function un() {
3472
3472
  }
3473
3473
  },
3474
3474
  execute: async (e, t) => {
3475
- let n = z(t, String(e.editor_ref ?? ""));
3475
+ let n = V(t, String(e.editor_ref ?? ""));
3476
3476
  if (!n) return {
3477
3477
  success: !1,
3478
3478
  error: `Editor "${e.editor_ref}" not found. Use list_editors to see available editors.`
@@ -3483,7 +3483,7 @@ function un() {
3483
3483
  };
3484
3484
  let r = (n.contents || "").split("\n").length;
3485
3485
  n.setContent(e.contents);
3486
- let i = await B(t);
3486
+ let i = await H(t);
3487
3487
  return i.success ? {
3488
3488
  success: !0,
3489
3489
  message: `Updated editor "${n.name}": ${r} -> ${e.contents.split("\n").length} lines.${i.suffix}`
@@ -3511,7 +3511,7 @@ function un() {
3511
3511
  }
3512
3512
  },
3513
3513
  execute: async (e, t) => {
3514
- let n = z(t, String(e.editor_ref ?? ""));
3514
+ let n = V(t, String(e.editor_ref ?? ""));
3515
3515
  if (!n) return {
3516
3516
  success: !1,
3517
3517
  error: `Editor "${e.editor_ref}" not found. Use list_editors to see available editors.`
@@ -3525,12 +3525,12 @@ function un() {
3525
3525
  error: `Editor "${n.name}" is currently running. Wait for it to finish or cancel it before deleting.`
3526
3526
  };
3527
3527
  n.delete();
3528
- let r = sn(t, n.id, "delete");
3528
+ let r = on(t, n.id, "delete");
3529
3529
  t.runtime.navigation?.closeTab(null, n.id);
3530
- let i = await B(t, r > 0);
3530
+ let i = await H(t, r > 0);
3531
3531
  return i.success ? {
3532
3532
  success: !0,
3533
- message: `Deleted editor "${n.name}" (id ${n.id}).${cn(r, "removed")}${i.suffix}`
3533
+ message: `Deleted editor "${n.name}" (id ${n.id}).${sn(r, "removed")}${i.suffix}`
3534
3534
  } : i;
3535
3535
  }
3536
3536
  },
@@ -3549,8 +3549,8 @@ function un() {
3549
3549
  }
3550
3550
  },
3551
3551
  execute: async (e, t) => {
3552
- let n = z(t, String(e.editor_ref ?? ""));
3553
- return n ? ln(t, n) : {
3552
+ let n = V(t, String(e.editor_ref ?? ""));
3553
+ return n ? cn(t, n) : {
3554
3554
  success: !1,
3555
3555
  error: `Editor "${e.editor_ref}" not found. Use list_editors to see available editors.`
3556
3556
  };
@@ -3576,13 +3576,13 @@ function un() {
3576
3576
  required: ["query", "connection"]
3577
3577
  }
3578
3578
  },
3579
- execute: (e, t) => Yt(t).validateQuery(String(e.query ?? ""), String(e.connection ?? ""))
3579
+ execute: (e, t) => qt(t).validateQuery(String(e.query ?? ""), String(e.connection ?? ""))
3580
3580
  }
3581
3581
  ];
3582
3582
  }
3583
3583
  //#endregion
3584
3584
  //#region llm/registry/packs/dashboardPack.ts
3585
- var dn = [
3585
+ var un = [
3586
3586
  "get_dashboard_state",
3587
3587
  "list_dashboard_items",
3588
3588
  "get_dashboard_item",
@@ -3596,7 +3596,7 @@ var dn = [
3596
3596
  "set_dashboard_theme",
3597
3597
  "capture_dashboard_screenshot"
3598
3598
  ];
3599
- function fn(e) {
3599
+ function dn(e) {
3600
3600
  return {
3601
3601
  ...e,
3602
3602
  input_schema: {
@@ -3611,10 +3611,10 @@ function fn(e) {
3611
3611
  }
3612
3612
  };
3613
3613
  }
3614
- function pn(e, t) {
3614
+ function fn(e, t) {
3615
3615
  return t || e.runtime.screenBridge?.dashboard?.dashboardId || e.runtime.navigation?.activeDashboard.value || "";
3616
3616
  }
3617
- function mn(e, t) {
3617
+ function pn(e, t) {
3618
3618
  let n = `dashboardExecutor:${t}`, r = e.session.cache.get(n);
3619
3619
  if (r) return r;
3620
3620
  let { dashboardStore: i, connectionStore: a, editorStore: o, chatStore: s, queryExecutionService: c } = e.runtime;
@@ -3630,7 +3630,7 @@ function mn(e, t) {
3630
3630
  queryExecutionService: c,
3631
3631
  llmConnectionStore: e.runtime.llmConnectionStore
3632
3632
  };
3633
- return r = new ht({
3633
+ return r = new pt({
3634
3634
  dashboardStore: i,
3635
3635
  connectionStore: a,
3636
3636
  editorStore: o,
@@ -3638,7 +3638,7 @@ function mn(e, t) {
3638
3638
  dashboardId: t,
3639
3639
  getActiveImports: () => {
3640
3640
  let e = i.dashboards[t];
3641
- return e ? M(e) : [];
3641
+ return e ? F(e) : [];
3642
3642
  },
3643
3643
  setActiveImports: (e) => {
3644
3644
  i.updateDashboardImports(t, e.map((e) => ({
@@ -3649,7 +3649,7 @@ function mn(e, t) {
3649
3649
  },
3650
3650
  getDashboardQueryExecutor: () => {
3651
3651
  let e = l();
3652
- return e ? e.getDashboardQueryExecutor() ?? null : Dt(t, u);
3652
+ return e ? e.getDashboardQueryExecutor() ?? null : Tt(t, u);
3653
3653
  },
3654
3654
  refreshItem: (e) => l()?.refreshItem(e) ?? void 0,
3655
3655
  captureDashboardImage: async () => {
@@ -3659,13 +3659,13 @@ function mn(e, t) {
3659
3659
  }
3660
3660
  }), e.session.cache.set(n, r), r;
3661
3661
  }
3662
- async function hn(e, t, n) {
3662
+ async function mn(e, t, n) {
3663
3663
  let { dashboard_id: r, ...i } = t;
3664
3664
  if (!n.runtime.dashboardStore) return {
3665
3665
  success: !1,
3666
3666
  error: "Dashboard store is not available in this context."
3667
3667
  };
3668
- let a = pn(n, r ? String(r) : void 0);
3668
+ let a = fn(n, r ? String(r) : void 0);
3669
3669
  if (!a) return {
3670
3670
  success: !1,
3671
3671
  error: "No dashboard is currently in view. Pass dashboard_id, or use open_dashboard / list_dashboards first."
@@ -3674,23 +3674,23 @@ async function hn(e, t, n) {
3674
3674
  return !o || o.deleted ? {
3675
3675
  success: !1,
3676
3676
  error: `Dashboard "${a}" not found.`
3677
- } : (kt.has(e) && (a = await At({
3677
+ } : (Dt.has(e) && (a = await Ot({
3678
3678
  dashboard: o,
3679
3679
  chatId: n.session.chatId,
3680
3680
  chatStore: n.runtime.chatStore,
3681
3681
  dashboardStore: n.runtime.dashboardStore,
3682
3682
  setActiveDashboard: n.runtime.navigation ? (e) => n.runtime.navigation.setActiveDashboard(e) : void 0
3683
- })), mn(n, a).executeToolCall(e, i));
3683
+ })), pn(n, a).executeToolCall(e, i));
3684
3684
  }
3685
- function gn() {
3685
+ function hn() {
3686
3686
  return [
3687
- ...dn.map((e) => {
3688
- let t = qe.find((t) => t.name === e);
3687
+ ...un.map((e) => {
3688
+ let t = j.find((t) => t.name === e);
3689
3689
  if (!t) throw Error(`DASHBOARD_TOOLS is missing expected tool '${e}'`);
3690
3690
  return {
3691
- definition: fn(t),
3691
+ definition: dn(t),
3692
3692
  pack: "dashboard",
3693
- execute: (t, n) => hn(e, t, n)
3693
+ execute: (t, n) => mn(e, t, n)
3694
3694
  };
3695
3695
  }),
3696
3696
  {
@@ -3790,7 +3790,7 @@ function gn() {
3790
3790
  hint: "No dashboard is mounted on screen. Use open_dashboard first, then retry."
3791
3791
  },
3792
3792
  execute: async (e, t) => {
3793
- let n = t.runtime.screenBridge?.dashboard, r = pn(t, e.dashboard_id ? String(e.dashboard_id) : void 0);
3793
+ let n = t.runtime.screenBridge?.dashboard, r = fn(t, e.dashboard_id ? String(e.dashboard_id) : void 0);
3794
3794
  if (!n || n.dashboardId !== r) return {
3795
3795
  success: !1,
3796
3796
  error: `Dashboard "${r}" is not the one on screen. Use open_dashboard first.`
@@ -3806,22 +3806,22 @@ function gn() {
3806
3806
  }
3807
3807
  //#endregion
3808
3808
  //#region llm/registry/packs/jobsPack.ts
3809
- var _n = 2e3, vn = 120, yn = 300;
3810
- function V(e) {
3809
+ var gn = 2e3, _n = 120, vn = 300;
3810
+ function U(e) {
3811
3811
  return e.runtime.jobsStore ? { available: !0 } : {
3812
3812
  available: !1,
3813
3813
  hint: "Job stores are not available in this context."
3814
3814
  };
3815
3815
  }
3816
- function H(e) {
3816
+ function W(e) {
3817
3817
  return `job ${e.job_id}: ${e.operation} "${e.target}" — status ${e.status}${e.error ? `, error: ${e.error}` : ""}${e.pollingState && e.pollingState !== "ok" ? ` (polling ${e.pollingState})` : ""}`;
3818
3818
  }
3819
- var bn = null, xn = () => (bn ||= new De(), bn);
3820
- function Sn() {
3819
+ var yn = null, bn = () => (yn ||= new Te(), yn);
3820
+ function xn() {
3821
3821
  return [
3822
3822
  {
3823
3823
  pack: "jobs",
3824
- availability: V,
3824
+ availability: U,
3825
3825
  definition: {
3826
3826
  name: "list_job_stores",
3827
3827
  description: "List remote model stores that support jobs (run/refresh of hosted targets), with their status.",
@@ -3843,7 +3843,7 @@ function Sn() {
3843
3843
  },
3844
3844
  {
3845
3845
  pack: "jobs",
3846
- availability: V,
3846
+ availability: U,
3847
3847
  definition: {
3848
3848
  name: "list_store_files",
3849
3849
  description: "List files/targets available in a job-capable store.",
@@ -3878,7 +3878,7 @@ function Sn() {
3878
3878
  },
3879
3879
  {
3880
3880
  pack: "jobs",
3881
- availability: V,
3881
+ availability: U,
3882
3882
  definition: {
3883
3883
  name: "submit_store_job",
3884
3884
  description: "Submit a job to a store: run or refresh a target (e.g. refresh a materialized parquet aggregate). Returns the job id; use await_job to wait for completion.",
@@ -3911,7 +3911,7 @@ function Sn() {
3911
3911
  try {
3912
3912
  return {
3913
3913
  success: !0,
3914
- message: `Submitted ${H(await n.submitJob(String(e.store_id), String(e.target), e.operation === "refresh" ? "refresh" : "run"))}. Use await_job to wait for completion.`
3914
+ message: `Submitted ${W(await n.submitJob(String(e.store_id), String(e.target), e.operation === "refresh" ? "refresh" : "run"))}. Use await_job to wait for completion.`
3915
3915
  };
3916
3916
  } catch (e) {
3917
3917
  return {
@@ -3923,7 +3923,7 @@ function Sn() {
3923
3923
  },
3924
3924
  {
3925
3925
  pack: "jobs",
3926
- availability: V,
3926
+ availability: U,
3927
3927
  definition: {
3928
3928
  name: "get_job_status",
3929
3929
  description: "Get the current status of a job by id.",
@@ -3946,7 +3946,7 @@ function Sn() {
3946
3946
  let n = t.runtime.jobsStore.getStoreJobs(String(e.store_id)).find((t) => t.job_id === String(e.job_id));
3947
3947
  return n ? {
3948
3948
  success: !0,
3949
- message: H(n)
3949
+ message: W(n)
3950
3950
  } : {
3951
3951
  success: !1,
3952
3952
  error: `Job ${e.job_id} not found in store ${e.store_id}.`
@@ -3955,7 +3955,7 @@ function Sn() {
3955
3955
  },
3956
3956
  {
3957
3957
  pack: "jobs",
3958
- availability: V,
3958
+ availability: U,
3959
3959
  definition: {
3960
3960
  name: "await_job",
3961
3961
  description: "Wait for a job to finish (or time out). Prefer this over polling get_job_status in a loop — one call, returns the final status.",
@@ -3972,14 +3972,14 @@ function Sn() {
3972
3972
  },
3973
3973
  timeout_seconds: {
3974
3974
  type: "number",
3975
- description: `Max seconds to wait (default ${vn}, max ${yn})`
3975
+ description: `Max seconds to wait (default ${_n}, max ${vn})`
3976
3976
  }
3977
3977
  },
3978
3978
  required: ["store_id", "job_id"]
3979
3979
  }
3980
3980
  },
3981
3981
  execute: async (e, t) => {
3982
- let n = t.runtime.jobsStore, r = String(e.store_id), i = String(e.job_id), a = Math.min(yn, Math.max(1, Number(e.timeout_seconds) || vn)), o = Date.now() + a * 1e3, s = () => n.getStoreJobs(r).find((e) => e.job_id === i), c = s();
3982
+ let n = t.runtime.jobsStore, r = String(e.store_id), i = String(e.job_id), a = Math.min(vn, Math.max(1, Number(e.timeout_seconds) || _n)), o = Date.now() + a * 1e3, s = () => n.getStoreJobs(r).find((e) => e.job_id === i), c = s();
3983
3983
  if (!c) return {
3984
3984
  success: !1,
3985
3985
  error: `Job ${i} not found in store ${r}.`
@@ -3987,19 +3987,19 @@ function Sn() {
3987
3987
  for (; c && c.status === "running" && Date.now() < o;) {
3988
3988
  if ((c.pollingState ?? "ok") !== "ok") return {
3989
3989
  success: !1,
3990
- error: `Stopped waiting: ${H(c)}. ${c.pollingError || ""}`.trim()
3990
+ error: `Stopped waiting: ${W(c)}. ${c.pollingError || ""}`.trim()
3991
3991
  };
3992
- await new Promise((e) => setTimeout(e, _n)), c = s();
3992
+ await new Promise((e) => setTimeout(e, gn)), c = s();
3993
3993
  }
3994
3994
  return c ? c.status === "running" ? {
3995
3995
  success: !1,
3996
- error: `Timed out after ${a}s: ${H(c)}. It continues in the background — call await_job or get_job_status again.`
3996
+ error: `Timed out after ${a}s: ${W(c)}. It continues in the background — call await_job or get_job_status again.`
3997
3997
  } : c.status === "success" ? {
3998
3998
  success: !0,
3999
- message: `Job finished: ${H(c)}`
3999
+ message: `Job finished: ${W(c)}`
4000
4000
  } : {
4001
4001
  success: !1,
4002
- error: `Job finished unsuccessfully: ${H(c)}`
4002
+ error: `Job finished unsuccessfully: ${W(c)}`
4003
4003
  } : {
4004
4004
  success: !1,
4005
4005
  error: `Job ${i} disappeared while waiting.`
@@ -4008,7 +4008,7 @@ function Sn() {
4008
4008
  },
4009
4009
  {
4010
4010
  pack: "jobs",
4011
- availability: V,
4011
+ availability: U,
4012
4012
  definition: {
4013
4013
  name: "get_store_state",
4014
4014
  description: "Get the freshness/state snapshot for a target in a job-capable store (e.g. when its data was last computed).",
@@ -4058,7 +4058,7 @@ function Sn() {
4058
4058
  },
4059
4059
  {
4060
4060
  pack: "jobs",
4061
- availability: V,
4061
+ availability: U,
4062
4062
  definition: {
4063
4063
  name: "list_jobs",
4064
4064
  description: "List recent jobs for a store, newest first.",
@@ -4084,7 +4084,7 @@ function Sn() {
4084
4084
  message: `No jobs recorded for store ${e.store_id}.`
4085
4085
  } : {
4086
4086
  success: !0,
4087
- message: i.map(H).join("\n")
4087
+ message: i.map(W).join("\n")
4088
4088
  };
4089
4089
  }
4090
4090
  },
@@ -4111,7 +4111,7 @@ function Sn() {
4111
4111
  execute: async (e, t) => {
4112
4112
  let n = Math.max(1, Math.floor(Number(e.limit) || 10));
4113
4113
  try {
4114
- let t = await xn().getQueriesForConnection(String(e.connection));
4114
+ let t = await bn().getQueriesForConnection(String(e.connection));
4115
4115
  return t.length ? {
4116
4116
  success: !0,
4117
4117
  message: t.slice(0, n).map((e) => {
@@ -4134,7 +4134,7 @@ function Sn() {
4134
4134
  }
4135
4135
  //#endregion
4136
4136
  //#region data/tutorial/docTypes.ts
4137
- var U = class {
4137
+ var G = class {
4138
4138
  title;
4139
4139
  content;
4140
4140
  type;
@@ -4142,43 +4142,43 @@ var U = class {
4142
4142
  constructor(e, t, n = null, r = null) {
4143
4143
  this.title = e, this.content = t, this.type = n, this.data = r || {};
4144
4144
  }
4145
- }, W = class {
4145
+ }, K = class {
4146
4146
  title;
4147
4147
  paragraphs;
4148
4148
  displayName;
4149
4149
  constructor(e, t, n = void 0) {
4150
4150
  this.title = e, this.paragraphs = t, this.displayName = n;
4151
4151
  }
4152
- }, G = class {
4152
+ }, q = class {
4153
4153
  title;
4154
4154
  articles;
4155
4155
  constructor(e, t) {
4156
4156
  this.title = e, this.articles = t;
4157
4157
  }
4158
- }, Cn = new W("Dashboards", [
4159
- new U("Purpose", "Trilogy is designed as a data consumption language, and one of the fastest ways to expore data is through visualization. The dashboarding system in Trilogy Studio is designed to be streamlined but powerful. The semantic model means that default selections can be tailored and rich."),
4160
- new U("Purpose", "It aspires to be good out of the box, with progressive disclosure of capability. It is designed to be extensible and feature requests are welcome."),
4161
- new U("Purpose", "We`ll use the demo model you set up in the first tutorial. If you haven`t done that yet, go back and run through the imports. Make sure you have a model and connection read to go below."),
4162
- new U("ModelList", "", "model-validator"),
4163
- new U("ConnectionList", "", "connection-validator"),
4164
- new U("Purpose", "Now that you have a connection and model set up, lets get charting! Dashboards are accessed through the dashboard icon the left nav, but we'll drop one in right below. The demo model comes with a default example. "),
4165
- new U("Purpose", "Dashboards are SQL centric - so mostly, you'll just be writing queries. The one special thing on a dashboard is the global where clause. The filter bar at the top defines this; it will be concatenated to the where clause of every query on the dashboard. This is a great way to filter all of your charts at once.\n You can also cross-filter by clicking on any element in the chart. This will be automatically enabled; you can toggle off cross-filtering behavior on a per-chart basis on the header. (Make it only filter otherse, or not filterable at all.)"),
4166
- new U("Purpose", "In the below dashboard, try the following: <br>-Click on a manufacturer to filter other views. <br>-Go to edit mode and update the markdown on the top. <br>-Add a new chart. <br>-Change the horizontal bar chart to a vertical."),
4167
- new U("Purpose", "", "dashboard"),
4168
- new U("Purpose", "When starting a new dashboard, you can either create a blank one or use a template. The templates are designed to be a good starting point for common use cases. You can also import dashboards from other users. Some of the public models will come with default dashboards."),
4169
- new U("Purpose", "Each chart can have their own query, so it's easy to define metrics in place. For dimensions, it's best to define them in the model so that automatic cross-filtering can be applied; if they are local to a chart, they cannot be applied to other views."),
4170
- new U("Purpose", "If you have an active LLM configured, the top search bar can be used for natural language filtering with the LLM - use ctrl+shift+enter."),
4171
- new U("Purpose", "You can only crossfilter if fields exist on all charts. It's best to define anything you want to filter on in the base dashboard file, and avoid aliasing base fields.", "tip"),
4172
- new U("Purpose", "To fix the formatting of fields, ensure they have the appropriate types and traits in the model. For example, marking a year as a year type will make line charts eligible. See the model tutorial for more details. ", "tip"),
4173
- new U("Purpose", "For missing features [such as chart types] or other issues, please open an issue on the <a href=\"https://github.com/trilogy-data/trilogy-studio-core/issues\" target=\"_blank\">studio GitHub repo</a>.")
4174
- ], "Dashboard Tutorial"), wn = new W("Model Tutorial", [
4175
- new U("Purpose", "The prior two tutorials cover how to query data and create dashboards. Both of those rely on the foundational concept of Trilogy - the model."),
4176
- new U("Purpose", "A model is simply a collection of editors that have been marked as sources. Any source can be imported by name into another editor, or multiple times under different aliases. Composition of models is how more complex models are built."),
4177
- new U("Purpose", "The core aspects of models are concepts and datasources. Concepts represent the abstract logical model of the data, while datasources represent physical instantiations of it."),
4178
- new U("Purpose", "These are loosely coupled, meaning that you can evolve or change your underlying database schema and simply update the datasources to point to the new tables and column names. Critically, queries operate on concepts so do not need to change."),
4179
- new U("Purpose", "Let's set one up. If you haven't already done the first tutorial, go back and create `my-first-editor` using that. We'll use that as a base."),
4180
- new U("Purpose", "", "editor-validator"),
4181
- new U("Tutorial Queries", "bsb", "tutorial-prompts", {
4158
+ }, Sn = new K("Dashboards", [
4159
+ new G("Purpose", "Trilogy is designed as a data consumption language, and one of the fastest ways to expore data is through visualization. The dashboarding system in Trilogy Studio is designed to be streamlined but powerful. The semantic model means that default selections can be tailored and rich."),
4160
+ new G("Purpose", "It aspires to be good out of the box, with progressive disclosure of capability. It is designed to be extensible and feature requests are welcome."),
4161
+ new G("Purpose", "We`ll use the demo model you set up in the first tutorial. If you haven`t done that yet, go back and run through the imports. Make sure you have a model and connection read to go below."),
4162
+ new G("ModelList", "", "model-validator"),
4163
+ new G("ConnectionList", "", "connection-validator"),
4164
+ new G("Purpose", "Now that you have a connection and model set up, lets get charting! Dashboards are accessed through the dashboard icon the left nav, but we'll drop one in right below. The demo model comes with a default example. "),
4165
+ new G("Purpose", "Dashboards are SQL centric - so mostly, you'll just be writing queries. The one special thing on a dashboard is the global where clause. The filter bar at the top defines this; it will be concatenated to the where clause of every query on the dashboard. This is a great way to filter all of your charts at once.\n You can also cross-filter by clicking on any element in the chart. This will be automatically enabled; you can toggle off cross-filtering behavior on a per-chart basis on the header. (Make it only filter otherse, or not filterable at all.)"),
4166
+ new G("Purpose", "In the below dashboard, try the following: <br>-Click on a manufacturer to filter other views. <br>-Go to edit mode and update the markdown on the top. <br>-Add a new chart. <br>-Change the horizontal bar chart to a vertical."),
4167
+ new G("Purpose", "", "dashboard"),
4168
+ new G("Purpose", "When starting a new dashboard, you can either create a blank one or use a template. The templates are designed to be a good starting point for common use cases. You can also import dashboards from other users. Some of the public models will come with default dashboards."),
4169
+ new G("Purpose", "Each chart can have their own query, so it's easy to define metrics in place. For dimensions, it's best to define them in the model so that automatic cross-filtering can be applied; if they are local to a chart, they cannot be applied to other views."),
4170
+ new G("Purpose", "If you have an active LLM configured, the top search bar can be used for natural language filtering with the LLM - use ctrl+shift+enter."),
4171
+ new G("Purpose", "You can only crossfilter if fields exist on all charts. It's best to define anything you want to filter on in the base dashboard file, and avoid aliasing base fields.", "tip"),
4172
+ new G("Purpose", "To fix the formatting of fields, ensure they have the appropriate types and traits in the model. For example, marking a year as a year type will make line charts eligible. See the model tutorial for more details. ", "tip"),
4173
+ new G("Purpose", "For missing features [such as chart types] or other issues, please open an issue on the <a href=\"https://github.com/trilogy-data/trilogy-studio-core/issues\" target=\"_blank\">studio GitHub repo</a>.")
4174
+ ], "Dashboard Tutorial"), Cn = new K("Model Tutorial", [
4175
+ new G("Purpose", "The prior two tutorials cover how to query data and create dashboards. Both of those rely on the foundational concept of Trilogy - the model."),
4176
+ new G("Purpose", "A model is simply a collection of editors that have been marked as sources. Any source can be imported by name into another editor, or multiple times under different aliases. Composition of models is how more complex models are built."),
4177
+ new G("Purpose", "The core aspects of models are concepts and datasources. Concepts represent the abstract logical model of the data, while datasources represent physical instantiations of it."),
4178
+ new G("Purpose", "These are loosely coupled, meaning that you can evolve or change your underlying database schema and simply update the datasources to point to the new tables and column names. Critically, queries operate on concepts so do not need to change."),
4179
+ new G("Purpose", "Let's set one up. If you haven't already done the first tutorial, go back and create `my-first-editor` using that. We'll use that as a base."),
4180
+ new G("Purpose", "", "editor-validator"),
4181
+ new G("Tutorial Queries", "bsb", "tutorial-prompts", {
4182
4182
  context: "model-tutorial",
4183
4183
  editorId: "sql-basic-editor",
4184
4184
  prompts: [
@@ -4212,40 +4212,40 @@ var U = class {
4212
4212
  }
4213
4213
  ]
4214
4214
  }),
4215
- new U("Purpose", "Review the standard library doc for always available traits.", "tip"),
4216
- new U("Purpose", "We've defined all the key parts of a model - a key, some properties, and a datasource to fetch. In a real database, you'll usually be working off an actual table. Let's try that out. Follow these steps:\n <ul>\n <li>1. Create a new duckdb connection called 'iris-data' using the connection list below.</li>\n <li>2. Copy the SQL command to create the table.</li>\n <li>3. Click the black doc icon to create a new SQL editor. This will take you to the editor!</li>\n <li>4. Paste in the code and run it.</li>\n <li>5. Click 'set editor as startup script' in the top right.</li>\n <li>6. Come back into this tutorial tab.</li>\n </ul>\n "),
4217
- new U("Purpose", "It's important to not miss step 5 - that way every time you connect, the table will be automatically populated!.", "tip"),
4218
- new U("Purpose", "DuckDB WASM CSV import of this file don't appear to work properly on Firefox or Safari as of early 2025. On those browsers, consider download the csv directly and importing it into duckdb using the drag box rather than using the read_csv SQL below.", "warning"),
4219
- new U("Purpose", "CREATE OR REPLACE TABLE iris_data AS select *, row_number() over () as pk FROM read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv');", "code"),
4220
- new U("Purpose", "", "connections"),
4221
- new U("Purpose", "Now we're ready to create a new model. The steps below will again take you out of this tutorial, but you can always come back here by clicking the 'documentation' icon on the left.\n <ul>\n <li>1. Click on the connection you have, and expand the memory database.</li>\n <li>2. Hit refresh if needed. On the row with the iris table, click the database icon to generate a new raw datasource file.</li>\n <li>3. In the open file, change pk to be a \"key\" instead of \"property\" and replace the placeholder tags with pk. (example: property pk.species string;)</li>\n <li>4. In the open file, add a grain clause below the field mapping - grain(pk)</li>\n <li>5. Click the 'set as source' in top right to make this importable by other files.</li>\n <li>6. Rename the file to 'iris'.</li>\n </ul>\n "),
4222
- new U("Purpose", "The grain clause defines the primary key of your datasource. In this case, we've added a surrogate key called pk. Grains are used to understand when information needs to be grouped, and by what. The property tag is used similarly. A datasource can have no grain, in which case it is a heap and most operations on it will require an extra group to deduplicate.\n ", "tip"),
4223
- new U("Purpose", "\nkey pk int; # surrogate primary key for the dataset\nproperty pk.sepal_length float;\nproperty pk.sepal_width float;\nproperty pk.petal_length float;\nproperty pk.petal_width float;\nproperty pk.species string;\n\ndatasource iris_data (\n sepal_length:sepal_length,\n sepal_width:sepal_width,\n petal_length:petal_length,\n petal_width:petal_width,\n species:species,\n pk:pk,\n)\ngrain (pk)\naddress iris_data;\n ", "code"),
4224
- new U("Purpose", "Make sure your file looks like the above. Now create a new trilogy editor by clicking the orange document icon by your connection. Copy the below code and run it! \n "),
4225
- new U("Purpose", "import iris;\nselect\n species,\n avg(petal_length) as avg_petal_length,\n avg(petal_width) as avg_petal_width\n;\n ", "code"),
4226
- new U("Purpose", "If you get any errors, double check names and that you have set the iris file as a source. If you get a \"no such table\" error, make sure you have run the startup script to create the table. \n ", "tip"),
4227
- new U("Purpose", "Make sure your file looks like the above. Now create a new trilogy editor by clicking the orange document icon by your connection. Copy the below code and run it! \n "),
4228
- new U("Further Reading", "Go to the model page under the trilogy reference section.\n ")
4229
- ], "Model Tutorial"), Tn = new W("Welcome", [
4230
- new U("Purpose", "Trilogy is a semantic-powered SQL - familiar SQL syntax that operates on a typed semantic layer, instead of tables. It should feel like SQL, but faster and more expressive. You can read more about the language in the Trilogy reference section."),
4231
- new U("Purpose", "Trilogy Studio is intended as an accessible demonstration of core language features, which would otherwise have a high barrier to experimentation. It is open source and feature requests and contributions are welcome."),
4232
- new U("Purpose", "In the rest of this tutorial, we will walk you through a demo-model - based on the TPC-H dataset - to get started. This will give you a feel for how Trilogy works and how you can use it to query data."),
4233
- new U("Purpose", "A \"model\" is a collection of associated Trilogy files that define a specific semantic layer. Models can be imported and exported, and are the primary way to share and reuse Trilogy code. In Trilogy Studio, a model will always be associated with a connection, representing a database backend. You can assign the same model to multiple connections for reuse. ", "tip"),
4234
- new U("Purpose", "Trilogy Studio has a built in model \"store\", containing a set of public models hosted on github. The demo model is one of those options. Let's get started! Search below for the demo-model, and click the import button to add it. You want to import it to a duckdb connection. Select \"New DuckDB\" and keep the default connection name."),
4235
- new U("Purpose", "<PLACEHOLDER>", "community-models"),
4236
- new U("ModelList", "", "model-validator"),
4237
- new U("Purpose", "Now that you have the model imported and associated with a connection, we just need to click the plug icon to connect to it."),
4238
- new U("Purpose", "If you had set up the connection first, you could click the connection name to toggle details, and click the model dropdown and select the \"demo-model\".", "tip"),
4239
- new U("Purpose", "You can also run normal SQL against a connection, using a 'sql' typed editor. This is a good way to debug or test queries that you are not sure how to write in Trilogy, or to manage databases.", "tip"),
4240
- new U("ConnectionList", "", "connections"),
4241
- new U("ConnectionList", "", "connection-validator"),
4242
- new U("Purpose", "When the above indicator is green, you are good to move on! Note that normally, you will manage connections through the connection panel available on the sidebar - this component is just embedded here as well for the tutorial.", "tip"),
4243
- new U("Purpose", "Now that you have a connection and model set up, you can start querying in an editor. Editors are grouped by their storage, then by connection. `Local` represents local storage in your browser. [Other storage, like Github, will be available in the future] Your imported model will have populated some default editors, but let's start with a new one. In the sidebar, click the \"...\" menu next to the demo-model-connection to expand its options, then create a new Trilogy editor named \"my-first-editor\"."),
4244
- new U("Purpose", "", "editors"),
4245
- new U("Purpose", "", "editor-validator"),
4246
- new U("Purpose", "When the above indicator is green, you are good to move on! Note again that normally editors are managed through the sidebar. You will also see that some editors are marked as sources - these are importable by other editors on the model. As these often are metadata only, you can use the toggle at the top to hide these if desired. Any editor can be made a source by toggling a button in the editor.", "tip"),
4247
- new U("Purpose", "Editors are your interactive SQL experience. They let you write Trilogy, validate it, run it, and look at the results. Running (ctrl-enter) will either execute the full editor or the highlighted chunk of code (with any imports). In the below tutorial editor, we'll run through a few queries and validate your results."),
4248
- new U("Tutorial Queries", "bsb", "tutorial-prompts", {
4215
+ new G("Purpose", "Review the standard library doc for always available traits.", "tip"),
4216
+ new G("Purpose", "We've defined all the key parts of a model - a key, some properties, and a datasource to fetch. In a real database, you'll usually be working off an actual table. Let's try that out. Follow these steps:\n <ul>\n <li>1. Create a new duckdb connection called 'iris-data' using the connection list below.</li>\n <li>2. Copy the SQL command to create the table.</li>\n <li>3. Click the black doc icon to create a new SQL editor. This will take you to the editor!</li>\n <li>4. Paste in the code and run it.</li>\n <li>5. Click 'set editor as startup script' in the top right.</li>\n <li>6. Come back into this tutorial tab.</li>\n </ul>\n "),
4217
+ new G("Purpose", "It's important to not miss step 5 - that way every time you connect, the table will be automatically populated!.", "tip"),
4218
+ new G("Purpose", "DuckDB WASM CSV import of this file don't appear to work properly on Firefox or Safari as of early 2025. On those browsers, consider download the csv directly and importing it into duckdb using the drag box rather than using the read_csv SQL below.", "warning"),
4219
+ new G("Purpose", "CREATE OR REPLACE TABLE iris_data AS select *, row_number() over () as pk FROM read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv');", "code"),
4220
+ new G("Purpose", "", "connections"),
4221
+ new G("Purpose", "Now we're ready to create a new model. The steps below will again take you out of this tutorial, but you can always come back here by clicking the 'documentation' icon on the left.\n <ul>\n <li>1. Click on the connection you have, and expand the memory database.</li>\n <li>2. Hit refresh if needed. On the row with the iris table, click the database icon to generate a new raw datasource file.</li>\n <li>3. In the open file, change pk to be a \"key\" instead of \"property\" and replace the placeholder tags with pk. (example: property pk.species string;)</li>\n <li>4. In the open file, add a grain clause below the field mapping - grain(pk)</li>\n <li>5. Click the 'set as source' in top right to make this importable by other files.</li>\n <li>6. Rename the file to 'iris'.</li>\n </ul>\n "),
4222
+ new G("Purpose", "The grain clause defines the primary key of your datasource. In this case, we've added a surrogate key called pk. Grains are used to understand when information needs to be grouped, and by what. The property tag is used similarly. A datasource can have no grain, in which case it is a heap and most operations on it will require an extra group to deduplicate.\n ", "tip"),
4223
+ new G("Purpose", "\nkey pk int; # surrogate primary key for the dataset\nproperty pk.sepal_length float;\nproperty pk.sepal_width float;\nproperty pk.petal_length float;\nproperty pk.petal_width float;\nproperty pk.species string;\n\ndatasource iris_data (\n sepal_length:sepal_length,\n sepal_width:sepal_width,\n petal_length:petal_length,\n petal_width:petal_width,\n species:species,\n pk:pk,\n)\ngrain (pk)\naddress iris_data;\n ", "code"),
4224
+ new G("Purpose", "Make sure your file looks like the above. Now create a new trilogy editor by clicking the orange document icon by your connection. Copy the below code and run it! \n "),
4225
+ new G("Purpose", "import iris;\nselect\n species,\n avg(petal_length) as avg_petal_length,\n avg(petal_width) as avg_petal_width\n;\n ", "code"),
4226
+ new G("Purpose", "If you get any errors, double check names and that you have set the iris file as a source. If you get a \"no such table\" error, make sure you have run the startup script to create the table. \n ", "tip"),
4227
+ new G("Purpose", "Make sure your file looks like the above. Now create a new trilogy editor by clicking the orange document icon by your connection. Copy the below code and run it! \n "),
4228
+ new G("Further Reading", "Go to the model page under the trilogy reference section.\n ")
4229
+ ], "Model Tutorial"), wn = new K("Welcome", [
4230
+ new G("Purpose", "Trilogy is a semantic-powered SQL - familiar SQL syntax that operates on a typed semantic layer, instead of tables. It should feel like SQL, but faster and more expressive. You can read more about the language in the Trilogy reference section."),
4231
+ new G("Purpose", "Trilogy Studio is intended as an accessible demonstration of core language features, which would otherwise have a high barrier to experimentation. It is open source and feature requests and contributions are welcome."),
4232
+ new G("Purpose", "In the rest of this tutorial, we will walk you through a demo-model - based on the TPC-H dataset - to get started. This will give you a feel for how Trilogy works and how you can use it to query data."),
4233
+ new G("Purpose", "A \"model\" is a collection of associated Trilogy files that define a specific semantic layer. Models can be imported and exported, and are the primary way to share and reuse Trilogy code. In Trilogy Studio, a model will always be associated with a connection, representing a database backend. You can assign the same model to multiple connections for reuse. ", "tip"),
4234
+ new G("Purpose", "Trilogy Studio has a built in model \"store\", containing a set of public models hosted on github. The demo model is one of those options. Let's get started! Search below for the demo-model, and click the import button to add it. You want to import it to a duckdb connection. Select \"New DuckDB\" and keep the default connection name."),
4235
+ new G("Purpose", "<PLACEHOLDER>", "community-models"),
4236
+ new G("ModelList", "", "model-validator"),
4237
+ new G("Purpose", "Now that you have the model imported and associated with a connection, we just need to click the plug icon to connect to it."),
4238
+ new G("Purpose", "If you had set up the connection first, you could click the connection name to toggle details, and click the model dropdown and select the \"demo-model\".", "tip"),
4239
+ new G("Purpose", "You can also run normal SQL against a connection, using a 'sql' typed editor. This is a good way to debug or test queries that you are not sure how to write in Trilogy, or to manage databases.", "tip"),
4240
+ new G("ConnectionList", "", "connections"),
4241
+ new G("ConnectionList", "", "connection-validator"),
4242
+ new G("Purpose", "When the above indicator is green, you are good to move on! Note that normally, you will manage connections through the connection panel available on the sidebar - this component is just embedded here as well for the tutorial.", "tip"),
4243
+ new G("Purpose", "Now that you have a connection and model set up, you can start querying in an editor. Editors are grouped by their storage, then by connection. `Local` represents local storage in your browser. [Other storage, like Github, will be available in the future] Your imported model will have populated some default editors, but let's start with a new one. In the sidebar, click the \"...\" menu next to the demo-model-connection to expand its options, then create a new Trilogy editor named \"my-first-editor\"."),
4244
+ new G("Purpose", "", "editors"),
4245
+ new G("Purpose", "", "editor-validator"),
4246
+ new G("Purpose", "When the above indicator is green, you are good to move on! Note again that normally editors are managed through the sidebar. You will also see that some editors are marked as sources - these are importable by other editors on the model. As these often are metadata only, you can use the toggle at the top to hide these if desired. Any editor can be made a source by toggling a button in the editor.", "tip"),
4247
+ new G("Purpose", "Editors are your interactive SQL experience. They let you write Trilogy, validate it, run it, and look at the results. Running (ctrl-enter) will either execute the full editor or the highlighted chunk of code (with any imports). In the below tutorial editor, we'll run through a few queries and validate your results."),
4248
+ new G("Tutorial Queries", "bsb", "tutorial-prompts", {
4249
4249
  context: "sql-tutorial",
4250
4250
  editorId: "sql-basic-editor",
4251
4251
  prompts: [
@@ -4272,20 +4272,20 @@ var U = class {
4272
4272
  }
4273
4273
  ]
4274
4274
  }),
4275
- new U("Purpose", "Thats the basics! You know how to run queries in an editor, how to import a model, and how to connect to a datasource.", "tip"),
4276
- new U("Purpose", "For the last query, could you figure out how to visualize the data? Editors support inline visualization for rapid introspection; to create more complicated visuals, check out the dashboarding tutorial up next.", "tip"),
4277
- new U("Purpose", "Feel free to explore from here. A good place to start is clicking on the editors icon in the left nav and exploring the pre-populated demo editors, which show more functionality. Alternatively, poke more around the docs!")
4278
- ], "Welcome and Query Tutorial"), En = new W("LLM Tutorial", [
4279
- new U("Connections", "LLMs can optionally be used to enhance the studio experience. The LLM screen is accessible on the left-hand nav and provides some basic validation that your LLM will work. LLMs is not required, but we aim to make the best use of them when available."),
4280
- new U("Connections", "Gemini has had some of the best results, but any lightweight fast model works well to build scaffolding quickly. You will need to QA answers.", "tip"),
4281
- new U("Connections", "Core LLM use cases are -query generation from natural language, dashboard generation from natural language, dashboard filtering with natural language, Feature requests welcome! To add an LLM, you will need an appropriate API key. Currently, we support OpenAI, Anthropic, and Google, but adding more is straightforward."),
4282
- new U("Connections", "To improve LLM performance, the first place to start is your model, as concept definitions are provided as primary context. Adding more specific concepts and - especially - descriptions to fields will help the model understand better. For complex calculations, consider pre-defining them so they can be easily selected by the model."),
4283
- new U("Connections", "Make sure to update the model to your favorite! Do this by selecting one from the dropdown and explicitly clicking update model - after first setting up an LLM. We don't know what models are available until you first connect.", "tip"),
4284
- new U("Managing Connections", "You can view current LLM connections below. Only one LLM connection can be the default at a time. You can set a default by clicking the star icon. The default LLM will be used for all LLM functionality. Try setting one up, confirm you can connect, select an appropriate model, and save. Then head to an editor or a dashboard to try it out!"),
4285
- new U("LLMList", "", "llm-connections"),
4286
- new U("LLMList", "The LLM experience is not tested on mobile.", "warning"),
4287
- new U("Purpose", "", "editor-validator"),
4288
- new U("Tutorial Queries", "There should be an edito here! Make sure you've completed the first tutorial!", "tutorial-prompts", {
4275
+ new G("Purpose", "Thats the basics! You know how to run queries in an editor, how to import a model, and how to connect to a datasource.", "tip"),
4276
+ new G("Purpose", "For the last query, could you figure out how to visualize the data? Editors support inline visualization for rapid introspection; to create more complicated visuals, check out the dashboarding tutorial up next.", "tip"),
4277
+ new G("Purpose", "Feel free to explore from here. A good place to start is clicking on the editors icon in the left nav and exploring the pre-populated demo editors, which show more functionality. Alternatively, poke more around the docs!")
4278
+ ], "Welcome and Query Tutorial"), Tn = new K("LLM Tutorial", [
4279
+ new G("Connections", "LLMs can optionally be used to enhance the studio experience. The LLM screen is accessible on the left-hand nav and provides some basic validation that your LLM will work. LLMs is not required, but we aim to make the best use of them when available."),
4280
+ new G("Connections", "Gemini has had some of the best results, but any lightweight fast model works well to build scaffolding quickly. You will need to QA answers.", "tip"),
4281
+ new G("Connections", "Core LLM use cases are -query generation from natural language, dashboard generation from natural language, dashboard filtering with natural language, Feature requests welcome! To add an LLM, you will need an appropriate API key. Currently, we support OpenAI, Anthropic, and Google, but adding more is straightforward."),
4282
+ new G("Connections", "To improve LLM performance, the first place to start is your model, as concept definitions are provided as primary context. Adding more specific concepts and - especially - descriptions to fields will help the model understand better. For complex calculations, consider pre-defining them so they can be easily selected by the model."),
4283
+ new G("Connections", "Make sure to update the model to your favorite! Do this by selecting one from the dropdown and explicitly clicking update model - after first setting up an LLM. We don't know what models are available until you first connect.", "tip"),
4284
+ new G("Managing Connections", "You can view current LLM connections below. Only one LLM connection can be the default at a time. You can set a default by clicking the star icon. The default LLM will be used for all LLM functionality. Try setting one up, confirm you can connect, select an appropriate model, and save. Then head to an editor or a dashboard to try it out!"),
4285
+ new G("LLMList", "", "llm-connections"),
4286
+ new G("LLMList", "The LLM experience is not tested on mobile.", "warning"),
4287
+ new G("Purpose", "", "editor-validator"),
4288
+ new G("Tutorial Queries", "There should be an edito here! Make sure you've completed the first tutorial!", "tutorial-prompts", {
4289
4289
  context: "sql-tutorial",
4290
4290
  editorId: "sql-basic-editor",
4291
4291
  prompts: [{
@@ -4302,12 +4302,12 @@ var U = class {
4302
4302
  validationFn: (e) => e.data?.some((e) => e.order_customer_nation_region_headquarters === "HQ4" && Math.round(e.total_revenue) === 451328736) ?? !1
4303
4303
  }]
4304
4304
  }),
4305
- new U("Connections", "LLMs operate on the imported concept context. Make sure you have a datasource selected on a dashboard, or add imports to an editor before typing your comment. Ctrl-shift-enter is the default LLM activation shortcut; typically you will highlight text and then run this.", "tip")
4306
- ]), K = new W("Functions", [
4307
- new U("Trilogy Built-in Functions", "Trilogy provides a comprehensive set of built-in functions that closely map to standard SQL functions. These functions enable data transformation, aggregation, and manipulation within your queries. They are categorized into aggregate, date/time, string, numeric, collection, and utility functions."),
4308
- new U("Function Usage & Concepts", "Each function definition includes its input requirements and output characteristics. Key information includes:\n <ul>\n <li><b>Input Types:</b> The allowed data types for each argument.</li>\n <li><b>Output Type:</b> The data type of the value returned by the function.</li>\n <li><b>Output Purpose:</b> Describes the conceptual role of the output.\n <ul>\n <li><b>PROPERTY:</b> Represents an attribute derived from existing data, typically associated with the grain of the input(s). Example: <code>UPPER(name)</code>.</li>\n <li><b>METRIC:</b> Represents a calculated value that summarizes data across rows, respecting the query's grouping. Example: <code>SUM(revenue)</code>.</li>\n <li><b>KEY:</b> Represents an identifier or a dimension used for joining or grouping, often changing the grain of the data. Example: <code>UNNEST(tags)</code>.</li>\n <li><b>CONSTANT:</b> Represents a fixed value, independent of the input data rows. Example: <code>CURRENT_DATE()</code>.</li>\n </ul>\n </li>\n </ul>"),
4309
- new U("Aggregate Functions", "Aggregate functions perform calculations across a set of input rows and return a single summary value for each group."),
4310
- new U("SUM", "Computes the sum of non-null numeric values within each group. Returns NULL if the group is empty or contains only NULL values.", "function", { function: {
4305
+ new G("Connections", "LLMs operate on the imported concept context. Make sure you have a datasource selected on a dashboard, or add imports to an editor before typing your comment. Ctrl-shift-enter is the default LLM activation shortcut; typically you will highlight text and then run this.", "tip")
4306
+ ]), J = new K("Functions", [
4307
+ new G("Trilogy Built-in Functions", "Trilogy provides a comprehensive set of built-in functions that closely map to standard SQL functions. These functions enable data transformation, aggregation, and manipulation within your queries. They are categorized into aggregate, date/time, string, numeric, collection, and utility functions."),
4308
+ new G("Function Usage & Concepts", "Each function definition includes its input requirements and output characteristics. Key information includes:\n <ul>\n <li><b>Input Types:</b> The allowed data types for each argument.</li>\n <li><b>Output Type:</b> The data type of the value returned by the function.</li>\n <li><b>Output Purpose:</b> Describes the conceptual role of the output.\n <ul>\n <li><b>PROPERTY:</b> Represents an attribute derived from existing data, typically associated with the grain of the input(s). Example: <code>UPPER(name)</code>.</li>\n <li><b>METRIC:</b> Represents a calculated value that summarizes data across rows, respecting the query's grouping. Example: <code>SUM(revenue)</code>.</li>\n <li><b>KEY:</b> Represents an identifier or a dimension used for joining or grouping, often changing the grain of the data. Example: <code>UNNEST(tags)</code>.</li>\n <li><b>CONSTANT:</b> Represents a fixed value, independent of the input data rows. Example: <code>CURRENT_DATE()</code>.</li>\n </ul>\n </li>\n </ul>"),
4309
+ new G("Aggregate Functions", "Aggregate functions perform calculations across a set of input rows and return a single summary value for each group."),
4310
+ new G("SUM", "Computes the sum of non-null numeric values within each group. Returns NULL if the group is empty or contains only NULL values.", "function", { function: {
4311
4311
  inputTypes: [
4312
4312
  "INTEGER",
4313
4313
  "FLOAT",
@@ -4318,19 +4318,19 @@ var U = class {
4318
4318
  outputPurpose: "METRIC",
4319
4319
  example: "SELECT SUM(order_total) AS total_revenue BY customer_id;"
4320
4320
  } }),
4321
- new U("COUNT", "Counts the number of non-null input rows within each group. Takes a single argument of any type.", "function", { function: {
4321
+ new G("COUNT", "Counts the number of non-null input rows within each group. Takes a single argument of any type.", "function", { function: {
4322
4322
  inputTypes: ["Any"],
4323
4323
  outputType: "INTEGER",
4324
4324
  outputPurpose: "METRIC",
4325
4325
  example: "SELECT COUNT(order_id) AS num_orders BY product_category;"
4326
4326
  } }),
4327
- new U("COUNT_DISTINCT", "Counts the number of distinct non-null input values within each group. Takes a single argument of any type.", "function", { function: {
4327
+ new G("COUNT_DISTINCT", "Counts the number of distinct non-null input values within each group. Takes a single argument of any type.", "function", { function: {
4328
4328
  inputTypes: ["Any"],
4329
4329
  outputType: "INTEGER",
4330
4330
  outputPurpose: "METRIC",
4331
4331
  example: "SELECT COUNT_DISTINCT(customer_id) AS unique_customers BY month;"
4332
4332
  } }),
4333
- new U("MAX", "Returns the maximum non-null value across all input rows within each group. Comparison follows standard data type rules.", "function", { function: {
4333
+ new G("MAX", "Returns the maximum non-null value across all input rows within each group. Comparison follows standard data type rules.", "function", { function: {
4334
4334
  inputTypes: [
4335
4335
  "INTEGER",
4336
4336
  "FLOAT",
@@ -4345,7 +4345,7 @@ var U = class {
4345
4345
  outputPurpose: "METRIC",
4346
4346
  example: "SELECT MAX(order_date) AS latest_order_date BY customer_id;"
4347
4347
  } }),
4348
- new U("MIN", "Returns the minimum non-null value across all input rows within each group. Comparison follows standard data type rules.", "function", { function: {
4348
+ new G("MIN", "Returns the minimum non-null value across all input rows within each group. Comparison follows standard data type rules.", "function", { function: {
4349
4349
  inputTypes: [
4350
4350
  "INTEGER",
4351
4351
  "FLOAT",
@@ -4359,7 +4359,7 @@ var U = class {
4359
4359
  outputPurpose: "METRIC",
4360
4360
  example: "SELECT MIN(order_date) AS first_order_date BY customer_id;"
4361
4361
  } }),
4362
- new U("AVG", "Computes the average (arithmetic mean) of non-null numeric values within each group. Returns NULL if the group is empty or contains only NULL values. The result is typically a FLOAT or NUMBER.", "function", { function: {
4362
+ new G("AVG", "Computes the average (arithmetic mean) of non-null numeric values within each group. Returns NULL if the group is empty or contains only NULL values. The result is typically a FLOAT or NUMBER.", "function", { function: {
4363
4363
  inputTypes: [
4364
4364
  "INTEGER",
4365
4365
  "FLOAT",
@@ -4370,20 +4370,20 @@ var U = class {
4370
4370
  outputPurpose: "METRIC",
4371
4371
  example: "SELECT AVG(product_price) AS avg_price BY category;"
4372
4372
  } }),
4373
- new U("Date/Time Functions", "Functions for manipulating and extracting information from date and time values."),
4374
- new U("CURRENT_DATE", "Returns the current date when the query begins execution. This value remains constant throughout the query.", "function", { function: {
4373
+ new G("Date/Time Functions", "Functions for manipulating and extracting information from date and time values."),
4374
+ new G("CURRENT_DATE", "Returns the current date when the query begins execution. This value remains constant throughout the query.", "function", { function: {
4375
4375
  inputTypes: [],
4376
4376
  outputType: "DATE",
4377
4377
  outputPurpose: "CONSTANT",
4378
4378
  example: "SELECT order_id WHERE order_date = CURRENT_DATE();"
4379
4379
  } }),
4380
- new U("CURRENT_DATETIME", "Returns the current date and time (timestamp) when the query begins execution. This value remains constant throughout the query.", "function", { function: {
4380
+ new G("CURRENT_DATETIME", "Returns the current date and time (timestamp) when the query begins execution. This value remains constant throughout the query.", "function", { function: {
4381
4381
  inputTypes: [],
4382
4382
  outputType: "DATETIME",
4383
4383
  outputPurpose: "CONSTANT",
4384
4384
  example: "SELECT event_id, CURRENT_DATETIME() AS processing_time;"
4385
4385
  } }),
4386
- new U("DATE", "Extracts the date part from a timestamp, datetime, or string, or casts a value to the DATE type. If the input is already a DATE, it is returned unchanged.", "function", { function: {
4386
+ new G("DATE", "Extracts the date part from a timestamp, datetime, or string, or casts a value to the DATE type. If the input is already a DATE, it is returned unchanged.", "function", { function: {
4387
4387
  inputTypes: [
4388
4388
  "DATE",
4389
4389
  "TIMESTAMP",
@@ -4394,7 +4394,7 @@ var U = class {
4394
4394
  outputPurpose: "PROPERTY",
4395
4395
  example: "SELECT DATE(user_signup_timestamp) AS signup_date;"
4396
4396
  } }),
4397
- new U("DATETIME", "Casts the input value (date, timestamp, or string) to the DATETIME type.", "function", { function: {
4397
+ new G("DATETIME", "Casts the input value (date, timestamp, or string) to the DATETIME type.", "function", { function: {
4398
4398
  inputTypes: [
4399
4399
  "DATE",
4400
4400
  "TIMESTAMP",
@@ -4405,7 +4405,7 @@ var U = class {
4405
4405
  outputPurpose: "PROPERTY",
4406
4406
  example: "SELECT DATETIME(event_date) AS event_datetime;"
4407
4407
  } }),
4408
- new U("TIMESTAMP", "Casts the input value (date, datetime, or string) to the TIMESTAMP type.", "function", { function: {
4408
+ new G("TIMESTAMP", "Casts the input value (date, datetime, or string) to the TIMESTAMP type.", "function", { function: {
4409
4409
  inputTypes: [
4410
4410
  "DATE",
4411
4411
  "TIMESTAMP",
@@ -4416,19 +4416,19 @@ var U = class {
4416
4416
  outputPurpose: "PROPERTY",
4417
4417
  example: "SELECT TIMESTAMP(log_entry_string) AS log_timestamp;"
4418
4418
  } }),
4419
- new U("DATE_TRUNCATE", "Truncates a date/time value to the specified precision ('year', 'month', 'day', etc.). Returns a value of the same type as the input (or DATE if truncating to day or higher from a timestamp/datetime).", "function", { function: {
4419
+ new G("DATE_TRUNCATE", "Truncates a date/time value to the specified precision ('year', 'month', 'day', etc.). Returns a value of the same type as the input (or DATE if truncating to day or higher from a timestamp/datetime).", "function", { function: {
4420
4420
  inputTypes: ["DATE/TIMESTAMP/DATETIME/STRING", "DATE_PART"],
4421
4421
  outputType: "Same as input type (usually DATE or DATETIME/TIMESTAMP)",
4422
4422
  outputPurpose: "PROPERTY",
4423
4423
  example: "SELECT DATE_TRUNCATE(created_at, month) AS creation_month;"
4424
4424
  } }),
4425
- new U("DATE_PART", "Extracts a specific subfield (e.g., 'year', 'month', 'day', 'hour', 'dow' for day of week) from a date/time value as an integer.", "function", { function: {
4425
+ new G("DATE_PART", "Extracts a specific subfield (e.g., 'year', 'month', 'day', 'hour', 'dow' for day of week) from a date/time value as an integer.", "function", { function: {
4426
4426
  inputTypes: ["DATE/TIMESTAMP/DATETIME/STRING", "DATE_PART"],
4427
4427
  outputType: "INTEGER (with potential trait)",
4428
4428
  outputPurpose: "PROPERTY",
4429
4429
  example: "SELECT DATE_PART(order_timestamp, hour) AS hour_of_order;"
4430
4430
  } }),
4431
- new U("DATE_ADD", "Adds a specified integer interval to a date/time value. The unit of the interval is specified by the date part argument (e.g., day, month, year).", "function", { function: {
4431
+ new G("DATE_ADD", "Adds a specified integer interval to a date/time value. The unit of the interval is specified by the date part argument (e.g., day, month, year).", "function", { function: {
4432
4432
  inputTypes: [
4433
4433
  "DATE/TIMESTAMP/DATETIME/STRING",
4434
4434
  "DATE_PART",
@@ -4438,7 +4438,7 @@ var U = class {
4438
4438
  outputPurpose: "PROPERTY",
4439
4439
  example: "SELECT DATE_ADD(start_date, day, 14) AS end_date;"
4440
4440
  } }),
4441
- new U("DATE_SUB", "Subtracts a specified integer interval from a date/time value. The unit of the interval is specified by the date part argument.", "function", { function: {
4441
+ new G("DATE_SUB", "Subtracts a specified integer interval from a date/time value. The unit of the interval is specified by the date part argument.", "function", { function: {
4442
4442
  inputTypes: [
4443
4443
  "DATE/TIMESTAMP/DATETIME/STRING",
4444
4444
  "DATE_PART",
@@ -4448,7 +4448,7 @@ var U = class {
4448
4448
  outputPurpose: "PROPERTY",
4449
4449
  example: "SELECT DATE_SUB(event_timestamp, hour, 3) AS reminder_time;"
4450
4450
  } }),
4451
- new U("DATE_DIFF", "Calculates the integer difference between two date/time values, expressed in the units specified by the date part argument.", "function", { function: {
4451
+ new G("DATE_DIFF", "Calculates the integer difference between two date/time values, expressed in the units specified by the date part argument.", "function", { function: {
4452
4452
  inputTypes: [
4453
4453
  "DATE/TIMESTAMP/DATETIME/STRING",
4454
4454
  "DATE/TIMESTAMP/DATETIME/STRING",
@@ -4458,13 +4458,13 @@ var U = class {
4458
4458
  outputPurpose: "PROPERTY",
4459
4459
  example: "SELECT DATE_DIFF(end_date, start_date, day) AS duration_days;"
4460
4460
  } }),
4461
- new U("UNIX_TO_TIMESTAMP", "Converts an integer Unix epoch timestamp (seconds since 1970-01-01 00:00:00 UTC) to a TIMESTAMP value.", "function", { function: {
4461
+ new G("UNIX_TO_TIMESTAMP", "Converts an integer Unix epoch timestamp (seconds since 1970-01-01 00:00:00 UTC) to a TIMESTAMP value.", "function", { function: {
4462
4462
  inputTypes: ["INTEGER"],
4463
4463
  outputType: "TIMESTAMP",
4464
4464
  outputPurpose: "PROPERTY",
4465
4465
  example: "SELECT UNIX_TO_TIMESTAMP(log_time_seconds) AS event_time;"
4466
4466
  } }),
4467
- new U("SECOND", "Extracts the second component (0-59) from a date/time value as an integer.", "function", { function: {
4467
+ new G("SECOND", "Extracts the second component (0-59) from a date/time value as an integer.", "function", { function: {
4468
4468
  inputTypes: [
4469
4469
  "DATE",
4470
4470
  "TIMESTAMP",
@@ -4475,7 +4475,7 @@ var U = class {
4475
4475
  outputPurpose: "PROPERTY",
4476
4476
  example: "SELECT SECOND(event_time) AS event_second;"
4477
4477
  } }),
4478
- new U("MINUTE", "Extracts the minute component (0-59) from a date/time value as an integer.", "function", { function: {
4478
+ new G("MINUTE", "Extracts the minute component (0-59) from a date/time value as an integer.", "function", { function: {
4479
4479
  inputTypes: [
4480
4480
  "DATE",
4481
4481
  "TIMESTAMP",
@@ -4486,7 +4486,7 @@ var U = class {
4486
4486
  outputPurpose: "PROPERTY",
4487
4487
  example: "SELECT MINUTE(event_time) AS event_minute;"
4488
4488
  } }),
4489
- new U("HOUR", "Extracts the hour component (0-23) from a date/time value as an integer.", "function", { function: {
4489
+ new G("HOUR", "Extracts the hour component (0-23) from a date/time value as an integer.", "function", { function: {
4490
4490
  inputTypes: [
4491
4491
  "DATE",
4492
4492
  "TIMESTAMP",
@@ -4497,7 +4497,7 @@ var U = class {
4497
4497
  outputPurpose: "PROPERTY",
4498
4498
  example: "SELECT HOUR(event_time) AS event_hour;"
4499
4499
  } }),
4500
- new U("DAY", "Extracts the day of the month component (1-31) from a date/time value as an integer.", "function", { function: {
4500
+ new G("DAY", "Extracts the day of the month component (1-31) from a date/time value as an integer.", "function", { function: {
4501
4501
  inputTypes: [
4502
4502
  "DATE",
4503
4503
  "TIMESTAMP",
@@ -4508,7 +4508,7 @@ var U = class {
4508
4508
  outputPurpose: "PROPERTY",
4509
4509
  example: "SELECT DAY(event_date) AS event_day_of_month;"
4510
4510
  } }),
4511
- new U("WEEK", "Extracts the ISO week number (1-53) from a date/time value as an integer.", "function", { function: {
4511
+ new G("WEEK", "Extracts the ISO week number (1-53) from a date/time value as an integer.", "function", { function: {
4512
4512
  inputTypes: [
4513
4513
  "DATE",
4514
4514
  "TIMESTAMP",
@@ -4519,7 +4519,7 @@ var U = class {
4519
4519
  outputPurpose: "PROPERTY",
4520
4520
  example: "SELECT WEEK(order_date) AS order_week_iso;"
4521
4521
  } }),
4522
- new U("MONTH", "Extracts the month component (1-12) from a date/time value as an integer.", "function", { function: {
4522
+ new G("MONTH", "Extracts the month component (1-12) from a date/time value as an integer.", "function", { function: {
4523
4523
  inputTypes: [
4524
4524
  "DATE",
4525
4525
  "TIMESTAMP",
@@ -4530,7 +4530,7 @@ var U = class {
4530
4530
  outputPurpose: "PROPERTY",
4531
4531
  example: "SELECT MONTH(signup_date) AS signup_month;"
4532
4532
  } }),
4533
- new U("QUARTER", "Extracts the quarter component (1-4) from a date/time value as an integer.", "function", { function: {
4533
+ new G("QUARTER", "Extracts the quarter component (1-4) from a date/time value as an integer.", "function", { function: {
4534
4534
  inputTypes: [
4535
4535
  "DATE",
4536
4536
  "TIMESTAMP",
@@ -4541,7 +4541,7 @@ var U = class {
4541
4541
  outputPurpose: "PROPERTY",
4542
4542
  example: "SELECT QUARTER(fiscal_date) AS fiscal_quarter;"
4543
4543
  } }),
4544
- new U("YEAR", "Extracts the year component from a date/time value as an integer.", "function", { function: {
4544
+ new G("YEAR", "Extracts the year component from a date/time value as an integer.", "function", { function: {
4545
4545
  inputTypes: [
4546
4546
  "DATE",
4547
4547
  "TIMESTAMP",
@@ -4552,7 +4552,7 @@ var U = class {
4552
4552
  outputPurpose: "PROPERTY",
4553
4553
  example: "SELECT YEAR(birth_date) AS birth_year;"
4554
4554
  } }),
4555
- new U("DAY_OF_WEEK", "Extracts the day of the week as an integer (e.g., Sunday=0 or 1, Monday=1 or 2, depending on locale/implementation - typically Sunday=0).", "function", { function: {
4555
+ new G("DAY_OF_WEEK", "Extracts the day of the week as an integer (e.g., Sunday=0 or 1, Monday=1 or 2, depending on locale/implementation - typically Sunday=0).", "function", { function: {
4556
4556
  inputTypes: [
4557
4557
  "DATE",
4558
4558
  "TIMESTAMP",
@@ -4563,38 +4563,38 @@ var U = class {
4563
4563
  outputPurpose: "PROPERTY",
4564
4564
  example: "SELECT DAY_OF_WEEK(transaction_date) AS transaction_dow;"
4565
4565
  } }),
4566
- new U("String Functions", "Functions for manipulating text strings."),
4567
- new U("SPLIT", "Splits the first string argument into an array of strings, using the second string argument as the delimiter.", "function", { function: {
4566
+ new G("String Functions", "Functions for manipulating text strings."),
4567
+ new G("SPLIT", "Splits the first string argument into an array of strings, using the second string argument as the delimiter.", "function", { function: {
4568
4568
  inputTypes: ["STRING", "STRING"],
4569
4569
  outputType: "LIST<STRING>",
4570
4570
  outputPurpose: "PROPERTY",
4571
4571
  example: "SELECT SPLIT(product_tags, \";\") AS tag_list;"
4572
4572
  } }),
4573
- new U("UPPER", "Converts the input string to all uppercase letters according to locale rules.", "function", { function: {
4573
+ new G("UPPER", "Converts the input string to all uppercase letters according to locale rules.", "function", { function: {
4574
4574
  inputTypes: ["STRING"],
4575
4575
  outputType: "STRING",
4576
4576
  outputPurpose: "PROPERTY",
4577
4577
  example: "SELECT UPPER(country_code) AS normalized_country;"
4578
4578
  } }),
4579
- new U("LOWER", "Converts the input string to all lowercase letters according to locale rules.", "function", { function: {
4579
+ new G("LOWER", "Converts the input string to all lowercase letters according to locale rules.", "function", { function: {
4580
4580
  inputTypes: ["STRING"],
4581
4581
  outputType: "STRING",
4582
4582
  outputPurpose: "PROPERTY",
4583
4583
  example: "SELECT LOWER(email_address) AS normalized_email;"
4584
4584
  } }),
4585
- new U("CONTAINS", "Returns true if the first string contains the second string as a substring, false otherwise. Case-sensitive.", "function", { function: {
4585
+ new G("CONTAINS", "Returns true if the first string contains the second string as a substring, false otherwise. Case-sensitive.", "function", { function: {
4586
4586
  inputTypes: ["STRING", "STRING"],
4587
4587
  outputType: "BOOL",
4588
4588
  outputPurpose: "PROPERTY",
4589
4589
  example: "SELECT description WHERE CONTAINS(description, \"urgent\");"
4590
4590
  } }),
4591
- new U("STRPOS", "Returns the starting position (1-based index) of the first occurrence of the second string within the first string. Returns 0 if the substring is not found. Case-sensitive.", "function", { function: {
4591
+ new G("STRPOS", "Returns the starting position (1-based index) of the first occurrence of the second string within the first string. Returns 0 if the substring is not found. Case-sensitive.", "function", { function: {
4592
4592
  inputTypes: ["STRING", "STRING"],
4593
4593
  outputType: "INTEGER",
4594
4594
  outputPurpose: "PROPERTY",
4595
4595
  example: "SELECT STRPOS(url, \"?\") AS query_start_pos;"
4596
4596
  } }),
4597
- new U("SUBSTRING", "Extracts a substring from the first argument, starting at the 1-based index specified by the second argument, for a length specified by the third argument.", "function", { function: {
4597
+ new G("SUBSTRING", "Extracts a substring from the first argument, starting at the 1-based index specified by the second argument, for a length specified by the third argument.", "function", { function: {
4598
4598
  inputTypes: [
4599
4599
  "STRING",
4600
4600
  "INTEGER",
@@ -4604,13 +4604,13 @@ var U = class {
4604
4604
  outputPurpose: "PROPERTY",
4605
4605
  example: "SELECT SUBSTRING(phone_number, 2, 3) AS area_code; # Assuming format like +1XXX..."
4606
4606
  } }),
4607
- new U("CONCAT", "Concatenates two or more string arguments together. NULL arguments are typically ignored or result in NULL depending on implementation.", "function", { function: {
4607
+ new G("CONCAT", "Concatenates two or more string arguments together. NULL arguments are typically ignored or result in NULL depending on implementation.", "function", { function: {
4608
4608
  inputTypes: ["STRING", "..."],
4609
4609
  outputType: "STRING",
4610
4610
  outputPurpose: "PROPERTY",
4611
4611
  example: "SELECT CONCAT(first_name, \" \", last_name) AS full_name;"
4612
4612
  } }),
4613
- new U("LENGTH", "Returns the length of a string (number of characters), the number of elements in an array/list, or the number of key-value pairs in a map.", "function", { function: {
4613
+ new G("LENGTH", "Returns the length of a string (number of characters), the number of elements in an array/list, or the number of key-value pairs in a map.", "function", { function: {
4614
4614
  inputTypes: [
4615
4615
  "STRING",
4616
4616
  "ARRAY",
@@ -4621,20 +4621,20 @@ var U = class {
4621
4621
  outputPurpose: "PROPERTY",
4622
4622
  example: "SELECT LENGTH(comment) AS comment_length;"
4623
4623
  } }),
4624
- new U("LIKE", "Performs case-sensitive SQL LIKE pattern matching. Use '%' for any sequence of zero or more characters and '_' for any single character.", "function", { function: {
4624
+ new G("LIKE", "Performs case-sensitive SQL LIKE pattern matching. Use '%' for any sequence of zero or more characters and '_' for any single character.", "function", { function: {
4625
4625
  inputTypes: ["STRING", "STRING"],
4626
4626
  outputType: "BOOL",
4627
4627
  outputPurpose: "PROPERTY",
4628
4628
  example: "SELECT product_name WHERE LIKE(product_name, \"Model %\");"
4629
4629
  } }),
4630
- new U("ILIKE", "Performs case-insensitive SQL LIKE pattern matching (similar to Postgres ILIKE). Use '%' and '_' wildcards as with LIKE.", "function", { function: {
4630
+ new G("ILIKE", "Performs case-insensitive SQL LIKE pattern matching (similar to Postgres ILIKE). Use '%' and '_' wildcards as with LIKE.", "function", { function: {
4631
4631
  inputTypes: ["STRING", "STRING"],
4632
4632
  outputType: "BOOL",
4633
4633
  outputPurpose: "PROPERTY",
4634
4634
  example: "SELECT email WHERE ILIKE(email, \"%@example.com\");"
4635
4635
  } }),
4636
- new U("Numeric Functions", "Functions for performing mathematical operations on numbers."),
4637
- new U("ABS", "Computes the absolute value of a numeric argument.", "function", { function: {
4636
+ new G("Numeric Functions", "Functions for performing mathematical operations on numbers."),
4637
+ new G("ABS", "Computes the absolute value of a numeric argument.", "function", { function: {
4638
4638
  inputTypes: [
4639
4639
  "INTEGER",
4640
4640
  "FLOAT",
@@ -4644,13 +4644,13 @@ var U = class {
4644
4644
  outputPurpose: "PROPERTY",
4645
4645
  example: "SELECT ABS(price_change) AS abs_price_change;"
4646
4646
  } }),
4647
- new U("ROUND", "Rounds the first numeric argument to the number of decimal places specified by the second integer argument. If the second argument is omitted or zero, rounds to the nearest integer.", "function", { function: {
4647
+ new G("ROUND", "Rounds the first numeric argument to the number of decimal places specified by the second integer argument. If the second argument is omitted or zero, rounds to the nearest integer.", "function", { function: {
4648
4648
  inputTypes: ["INTEGER/FLOAT/NUMBER/NUMERIC", "INTEGER"],
4649
4649
  outputType: "Same as first input type",
4650
4650
  outputPurpose: "PROPERTY",
4651
4651
  example: "SELECT ROUND(average_rating, 1) AS rounded_rating;"
4652
4652
  } }),
4653
- new U("SQRT", "Calculates the square root of a non-negative numeric argument. The result is typically a FLOAT or NUMBER.", "function", { function: {
4653
+ new G("SQRT", "Calculates the square root of a non-negative numeric argument. The result is typically a FLOAT or NUMBER.", "function", { function: {
4654
4654
  inputTypes: [
4655
4655
  "INTEGER",
4656
4656
  "FLOAT",
@@ -4661,80 +4661,80 @@ var U = class {
4661
4661
  outputPurpose: "PROPERTY",
4662
4662
  example: "SELECT SQRT(variance) AS standard_deviation;"
4663
4663
  } }),
4664
- new U("RANDOM", "Returns a random floating-point value between 0.0 (inclusive) and 1.0 (exclusive) at the grain of the provided concept. If no concept is provided, all rows have the same value.", "function", { function: {
4664
+ new G("RANDOM", "Returns a random floating-point value between 0.0 (inclusive) and 1.0 (exclusive) at the grain of the provided concept. If no concept is provided, all rows have the same value.", "function", { function: {
4665
4665
  inputTypes: ["ANY"],
4666
4666
  outputType: "FLOAT",
4667
4667
  outputPurpose: "PROPERTY",
4668
4668
  example: "SELECT order_id, RANDOM(order_id) AS random_sort_key ORDER BY random_sort_key;"
4669
4669
  } }),
4670
- new U("ADD", "Performs arithmetic addition on two or more numeric arguments.", "function", { function: {
4670
+ new G("ADD", "Performs arithmetic addition on two or more numeric arguments.", "function", { function: {
4671
4671
  inputTypes: ["INTEGER/FLOAT/NUMBER/NUMERIC", "..."],
4672
4672
  outputType: "Appropriate numeric type based on inputs",
4673
4673
  outputPurpose: "PROPERTY",
4674
4674
  example: "SELECT ADD(subtotal, tax, shipping_fee) AS grand_total;"
4675
4675
  } }),
4676
- new U("SUBTRACT", "Performs arithmetic subtraction. If two arguments, subtracts the second from the first. If more, behavior might vary (e.g., subtract all subsequent from first).", "function", { function: {
4676
+ new G("SUBTRACT", "Performs arithmetic subtraction. If two arguments, subtracts the second from the first. If more, behavior might vary (e.g., subtract all subsequent from first).", "function", { function: {
4677
4677
  inputTypes: ["INTEGER/FLOAT/NUMBER/NUMERIC", "..."],
4678
4678
  outputType: "Appropriate numeric type based on inputs",
4679
4679
  outputPurpose: "PROPERTY",
4680
4680
  example: "SELECT SUBTRACT(total_revenue, total_cost) AS profit;"
4681
4681
  } }),
4682
- new U("MULTIPLY", "Performs arithmetic multiplication on two or more numeric arguments.", "function", { function: {
4682
+ new G("MULTIPLY", "Performs arithmetic multiplication on two or more numeric arguments.", "function", { function: {
4683
4683
  inputTypes: ["INTEGER/FLOAT/NUMBER/NUMERIC", "..."],
4684
4684
  outputType: "Appropriate numeric type based on inputs",
4685
4685
  outputPurpose: "PROPERTY",
4686
4686
  example: "SELECT MULTIPLY(quantity, unit_price) AS line_item_total;"
4687
4687
  } }),
4688
- new U("DIVIDE", "Performs arithmetic division. If two arguments, divides the first by the second. Division by zero typically results in an error or NULL/Infinity. Result type is often FLOAT or NUMBER.", "function", { function: {
4688
+ new G("DIVIDE", "Performs arithmetic division. If two arguments, divides the first by the second. Division by zero typically results in an error or NULL/Infinity. Result type is often FLOAT or NUMBER.", "function", { function: {
4689
4689
  inputTypes: ["INTEGER/FLOAT/NUMBER/NUMERIC", "..."],
4690
4690
  outputType: "FLOAT or NUMBER (usually)",
4691
4691
  outputPurpose: "PROPERTY",
4692
4692
  example: "SELECT DIVIDE(total_sales, number_of_units) AS price_per_unit;"
4693
4693
  } }),
4694
- new U("MOD", "Computes the modulo (remainder) of the first numeric argument divided by the second integer argument.", "function", { function: {
4694
+ new G("MOD", "Computes the modulo (remainder) of the first numeric argument divided by the second integer argument.", "function", { function: {
4695
4695
  inputTypes: ["INTEGER/FLOAT/NUMBER/NUMERIC", "INTEGER"],
4696
4696
  outputType: "INTEGER",
4697
4697
  outputPurpose: "PROPERTY",
4698
4698
  example: "SELECT user_id WHERE MOD(user_id, 2) = 0; # Even user IDs"
4699
4699
  } }),
4700
- new U("Utility Functions", "General-purpose functions for data manipulation, control flow, and type handling."),
4701
- new U("ALIAS", "Assigns a new name (alias) to an expression or concept without changing its value, type, or purpose. Useful for clarity or renaming in SELECT clauses.", "function", { function: {
4700
+ new G("Utility Functions", "General-purpose functions for data manipulation, control flow, and type handling."),
4701
+ new G("ALIAS", "Assigns a new name (alias) to an expression or concept without changing its value, type, or purpose. Useful for clarity or renaming in SELECT clauses.", "function", { function: {
4702
4702
  inputTypes: ["Any"],
4703
4703
  outputType: "Same as input",
4704
4704
  outputPurpose: "Same as input",
4705
4705
  example: "SELECT user.profile.first_name ALIAS fname;"
4706
4706
  } }),
4707
- new U("PARENTHETICAL", "Used for explicit grouping of expressions, primarily to control the order of operations. Does not change the value, type, or purpose of the enclosed expression.", "function", { function: {
4707
+ new G("PARENTHETICAL", "Used for explicit grouping of expressions, primarily to control the order of operations. Does not change the value, type, or purpose of the enclosed expression.", "function", { function: {
4708
4708
  inputTypes: ["Any"],
4709
4709
  outputType: "Same as input",
4710
4710
  outputPurpose: "Same as input",
4711
4711
  example: "SELECT (price + tax) * quantity AS total;"
4712
4712
  } }),
4713
- new U("COALESCE", "Returns the first non-null value from its list of arguments. All arguments must be of compatible types. Returns NULL if all arguments are NULL.", "function", { function: {
4713
+ new G("COALESCE", "Returns the first non-null value from its list of arguments. All arguments must be of compatible types. Returns NULL if all arguments are NULL.", "function", { function: {
4714
4714
  inputTypes: ["Any (compatible types)", "..."],
4715
4715
  outputType: "Same as input types (common compatible type)",
4716
4716
  outputPurpose: "PROPERTY",
4717
4717
  example: "SELECT COALESCE(nickname, first_name, \"Valued Customer\") AS display_name;"
4718
4718
  } }),
4719
- new U("CAST", "Explicitly converts an expression to a target data type specified as the second argument (using a DataType enum value or potentially a string literal).", "function", { function: {
4719
+ new G("CAST", "Explicitly converts an expression to a target data type specified as the second argument (using a DataType enum value or potentially a string literal).", "function", { function: {
4720
4720
  inputTypes: ["Any", "DataType"],
4721
4721
  outputType: "Specified by second argument (DataType)",
4722
4722
  outputPurpose: "PROPERTY",
4723
4723
  example: "SELECT CAST(order_id_string AS INTEGER) AS order_id_num;"
4724
4724
  } }),
4725
- new U("IS_NULL", "Returns true if the input argument is NULL, false otherwise.", "function", { function: {
4725
+ new G("IS_NULL", "Returns true if the input argument is NULL, false otherwise.", "function", { function: {
4726
4726
  inputTypes: ["Any"],
4727
4727
  outputType: "BOOL",
4728
4728
  outputPurpose: "PROPERTY",
4729
4729
  example: "SELECT order_id WHERE IS_NULL(shipping_address);"
4730
4730
  } }),
4731
- new U("BOOL", "Attempts to convert the input argument to a boolean value. Behavior depends on the input type (e.g., 0 -> false, non-zero -> true; empty string -> false, non-empty -> true).", "function", { function: {
4731
+ new G("BOOL", "Attempts to convert the input argument to a boolean value. Behavior depends on the input type (e.g., 0 -> false, non-zero -> true; empty string -> false, non-empty -> true).", "function", { function: {
4732
4732
  inputTypes: ["Any"],
4733
4733
  outputType: "BOOL",
4734
4734
  outputPurpose: "PROPERTY",
4735
4735
  example: "SELECT product_id, BOOL(is_active_flag) AS is_active;"
4736
4736
  } }),
4737
- new U("CASE", "Evaluates a series of conditions and returns a result based on the first condition that evaluates to true. Syntax: `CASE WHEN condition1 THEN result1 [WHEN condition2 THEN result2 ...] [ELSE default_result] END`. All result expressions must have compatible types.", "function", { function: {
4737
+ new G("CASE", "Evaluates a series of conditions and returns a result based on the first condition that evaluates to true. Syntax: `CASE WHEN condition1 THEN result1 [WHEN condition2 THEN result2 ...] [ELSE default_result] END`. All result expressions must have compatible types.", "function", { function: {
4738
4738
  inputTypes: [
4739
4739
  "BOOL",
4740
4740
  "Any",
@@ -4744,44 +4744,44 @@ var U = class {
4744
4744
  outputPurpose: "PROPERTY",
4745
4745
  example: "SELECT CASE WHEN score > 90 THEN \"A\" WHEN score > 80 THEN \"B\" ELSE \"C\" END AS grade;"
4746
4746
  } }),
4747
- new U("CONSTANT", "Represents a literal value provided as an argument. The type and purpose are determined by the literal itself.", "function", { function: {
4747
+ new G("CONSTANT", "Represents a literal value provided as an argument. The type and purpose are determined by the literal itself.", "function", { function: {
4748
4748
  inputTypes: ["Literal (String, Number, Bool, etc.)"],
4749
4749
  outputType: "Type of the literal",
4750
4750
  outputPurpose: "CONSTANT",
4751
4751
  example: "SELECT \"Default Category\" AS category, product_id;"
4752
4752
  } }),
4753
- new U("CUSTOM", "Represents a user-defined or engine-specific function not built into the core Trilogy language. Input types, output type, and behavior depend on the specific custom function implementation.", "function", { function: {
4753
+ new G("CUSTOM", "Represents a user-defined or engine-specific function not built into the core Trilogy language. Input types, output type, and behavior depend on the specific custom function implementation.", "function", { function: {
4754
4754
  inputTypes: ["Depends on custom function"],
4755
4755
  outputType: "Depends on custom function",
4756
4756
  outputPurpose: "Usually PROPERTY or METRIC",
4757
4757
  example: "SELECT my_custom_scoring_function(user_data) AS score;"
4758
4758
  } }),
4759
- new U("Collection Functions", "Functions for working with complex data types like arrays/lists, maps, and structs."),
4760
- new U("UNNEST", "Expands an array or list into a set of rows, where each row contains one element from the array/list. This typically changes the grain of the query, hence the KEY purpose.", "function", { function: {
4759
+ new G("Collection Functions", "Functions for working with complex data types like arrays/lists, maps, and structs."),
4760
+ new G("UNNEST", "Expands an array or list into a set of rows, where each row contains one element from the array/list. This typically changes the grain of the query, hence the KEY purpose.", "function", { function: {
4761
4761
  inputTypes: ["ARRAY", "LIST"],
4762
4762
  outputType: "Element type of the input collection",
4763
4763
  outputPurpose: "KEY",
4764
4764
  example: "SELECT order_id, UNNEST(product_ids) AS product_id;"
4765
4765
  } }),
4766
- new U("INDEX_ACCESS", "Retrieves an element from an array or list using its zero-based integer index. Accessing an index outside the bounds may result in NULL or an error.", "function", { function: {
4766
+ new G("INDEX_ACCESS", "Retrieves an element from an array or list using its zero-based integer index. Accessing an index outside the bounds may result in NULL or an error.", "function", { function: {
4767
4767
  inputTypes: ["ARRAY/LIST", "INTEGER"],
4768
4768
  outputType: "Element type of the input collection",
4769
4769
  outputPurpose: "PROPERTY",
4770
4770
  example: "SELECT INDEX_ACCESS(sensor_readings, 0) AS first_reading;"
4771
4771
  } }),
4772
- new U("MAP_ACCESS", "Retrieves the value associated with a specified key from a map. The key type must match the map's key type (usually STRING or INTEGER). Returns NULL if the key is not found.", "function", { function: {
4772
+ new G("MAP_ACCESS", "Retrieves the value associated with a specified key from a map. The key type must match the map's key type (usually STRING or INTEGER). Returns NULL if the key is not found.", "function", { function: {
4773
4773
  inputTypes: ["MAP", "INTEGER/STRING"],
4774
4774
  outputType: "Value type of the map",
4775
4775
  outputPurpose: "PROPERTY",
4776
4776
  example: "SELECT MAP_ACCESS(user_properties, \"preferred_language\") AS language;"
4777
4777
  } }),
4778
- new U("ATTR_ACCESS", "Retrieves the value of a named field (attribute) from a struct. The second argument is the attribute name (string). Accessing a non-existent attribute may result in NULL or an error.", "function", { function: {
4778
+ new G("ATTR_ACCESS", "Retrieves the value of a named field (attribute) from a struct. The second argument is the attribute name (string). Accessing a non-existent attribute may result in NULL or an error.", "function", { function: {
4779
4779
  inputTypes: ["STRUCT", "STRING"],
4780
4780
  outputType: "Type of the accessed attribute",
4781
4781
  outputPurpose: "PROPERTY",
4782
4782
  example: "SELECT ATTR_ACCESS(address_struct, \"zip_code\") AS zip;"
4783
4783
  } }),
4784
- new U("STRUCT", "Constructs a struct (similar to a record or object) dynamically from pairs of arguments: field name (string literal) followed by the field value.", "function", { function: {
4784
+ new G("STRUCT", "Constructs a struct (similar to a record or object) dynamically from pairs of arguments: field name (string literal) followed by the field value.", "function", { function: {
4785
4785
  inputTypes: [
4786
4786
  "STRING (name)",
4787
4787
  "Any (value)",
@@ -4791,20 +4791,20 @@ var U = class {
4791
4791
  outputPurpose: "PROPERTY",
4792
4792
  example: "SELECT STRUCT(\"id\", user_id, \"email\", user_email) AS user_info;"
4793
4793
  } }),
4794
- new U("ARRAY", "Constructs an array containing the provided arguments. All arguments should ideally be of a compatible type; behavior/support depends on target DB. The output type depends on the input argument types.", "function", { function: {
4794
+ new G("ARRAY", "Constructs an array containing the provided arguments. All arguments should ideally be of a compatible type; behavior/support depends on target DB. The output type depends on the input argument types.", "function", { function: {
4795
4795
  inputTypes: ["Any (compatible types)", "..."],
4796
4796
  outputType: "ARRAY of common compatible type of arguments",
4797
4797
  outputPurpose: "PROPERTY",
4798
4798
  example: "SELECT ARRAY(primary_color, secondary_color) AS palette;"
4799
4799
  } }),
4800
- new U("Set/Relational Functions", "Functions operating on sets or relations."),
4801
- new U("UNION", "Combines the results of two or more compatible inputs (concepts or queries) into a single result set. Behaves like UNION ALL (does not remove duplicates). The inputs must have compatible data types.", "function", { function: {
4800
+ new G("Set/Relational Functions", "Functions operating on sets or relations."),
4801
+ new G("UNION", "Combines the results of two or more compatible inputs (concepts or queries) into a single result set. Behaves like UNION ALL (does not remove duplicates). The inputs must have compatible data types.", "function", { function: {
4802
4802
  inputTypes: ["Any (compatible concepts/queries)", "..."],
4803
4803
  outputType: "Depends on inputs (structure of the combined set)",
4804
4804
  outputPurpose: "KEY",
4805
4805
  example: "SELECT * FROM UNION(active_customers, recently_lapsed_customers);"
4806
4806
  } }),
4807
- new U("GROUP", "Explicitly specifies the grouping keys for an expression, often used with aggregate functions when a different grain is needed than the main query `BY` clause. The first argument is the expression to aggregate/group, subsequent arguments are the keys. Syntax is distinct in that is is group key by <over clause>, similar to a group by.", "function", { function: {
4807
+ new G("GROUP", "Explicitly specifies the grouping keys for an expression, often used with aggregate functions when a different grain is needed than the main query `BY` clause. The first argument is the expression to aggregate/group, subsequent arguments are the keys. Syntax is distinct in that is is group key by <over clause>, similar to a group by.", "function", { function: {
4808
4808
  inputTypes: [
4809
4809
  "Any (expression)",
4810
4810
  "Any (key)",
@@ -4814,233 +4814,244 @@ var U = class {
4814
4814
  outputPurpose: "Depends on first argument",
4815
4815
  example: "SELECT SUM(revenue) AS total_revenue, GROUP SUM(revenue) BY product_category AS revenue_by_category, customer_id;"
4816
4816
  } })
4817
- ]), q = K.paragraphs.findIndex((e) => e.title === "Aggregate Functions"), J = K.paragraphs.findIndex((e) => e.title === "Date/Time Functions");
4818
- if (q < 0 || J < 0) throw Error("Built-in function documentation sections are incomplete");
4819
- var Dn = K.paragraphs[1], On = new W("Aggregate Functions", [
4820
- K.paragraphs[q],
4817
+ ]), En = J.paragraphs.findIndex((e) => e.title === "Aggregate Functions"), Y = J.paragraphs.findIndex((e) => e.title === "Date/Time Functions");
4818
+ if (En < 0 || Y < 0) throw Error("Built-in function documentation sections are incomplete");
4819
+ var Dn = J.paragraphs[1], On = new K("Aggregate Functions", [
4820
+ J.paragraphs[En],
4821
4821
  Dn,
4822
- ...K.paragraphs.slice(q + 1, J)
4823
- ]), kn = new W("Functions", [
4824
- new U("Trilogy Built-in Functions", "Trilogy provides built-in scalar functions for transforming and manipulating individual values. They are categorized into date/time, string, numeric, collection, and utility functions. Aggregate functions, which summarize groups of rows, are documented separately."),
4822
+ ...J.paragraphs.slice(En + 1, Y)
4823
+ ]), kn = new K("Functions", [
4824
+ new G("Trilogy Built-in Functions", "Trilogy provides built-in scalar functions for transforming and manipulating individual values. They are categorized into date/time, string, numeric, collection, and utility functions. Aggregate functions, which summarize groups of rows, are documented separately."),
4825
4825
  Dn,
4826
- ...K.paragraphs.slice(J)
4827
- ]), An = new W("Window Functions", [
4828
- new U("Trilogy Built-in Functions", "Window functions are a set of built-in functions in the Trilogy query language that allow you to perform calculations across a set of rows related to the current row. These functions are essential for data analysis, reporting, and complex aggregations."),
4829
- new U("Function Usage & Concepts", "Each function definition includes its input requirements and output characteristics. Key information includes:\n <ul>\n <li><b>Input Types:</b> The allowed data types for each argument.</li>\n <li><b>Output Type:</b> The data type of the value returned by the function.</li>\n <li><b>Output Purpose:</b> Describes the conceptual role of the output.\n <ul>\n <li><b>PROPERTY:</b> Represents an attribute derived from existing data, typically associated with the grain of the input(s). Example: <code>UPPER(name)</code>.</li>\n <li><b>METRIC:</b> Represents a calculated value that summarizes data across rows, respecting the query's grouping. Example: <code>SUM(revenue)</code>.</li>\n <li><b>KEY:</b> Represents an identifier or a dimension used for joining or grouping, often changing the grain of the data. Example: <code>UNNEST(tags)</code>.</li>\n <li><b>CONSTANT:</b> Represents a fixed value, independent of the input data rows. Example: <code>CURRENT_DATE()</code>.</li>\n </ul>\n </li>\n </ul>"),
4830
- new U("RANK", "Computes the rank of each row within its partition, with ties receiving the same rank. The rank starts at 1 for the first row in each partition.", "function", { function: {
4826
+ ...J.paragraphs.slice(Y)
4827
+ ]), An = new K("Window Functions", [
4828
+ new G("Trilogy Built-in Functions", "Window functions are a set of built-in functions in the Trilogy query language that allow you to perform calculations across a set of rows related to the current row. These functions are essential for data analysis, reporting, and complex aggregations."),
4829
+ new G("Function Usage & Concepts", "Each function definition includes its input requirements and output characteristics. Key information includes:\n <ul>\n <li><b>Input Types:</b> The allowed data types for each argument.</li>\n <li><b>Output Type:</b> The data type of the value returned by the function.</li>\n <li><b>Output Purpose:</b> Describes the conceptual role of the output.\n <ul>\n <li><b>PROPERTY:</b> Represents an attribute derived from existing data, typically associated with the grain of the input(s). Example: <code>UPPER(name)</code>.</li>\n <li><b>METRIC:</b> Represents a calculated value that summarizes data across rows, respecting the query's grouping. Example: <code>SUM(revenue)</code>.</li>\n <li><b>KEY:</b> Represents an identifier or a dimension used for joining or grouping, often changing the grain of the data. Example: <code>UNNEST(tags)</code>.</li>\n <li><b>CONSTANT:</b> Represents a fixed value, independent of the input data rows. Example: <code>CURRENT_DATE()</code>.</li>\n </ul>\n </li>\n </ul>"),
4830
+ new G("RANK", "Computes the rank of each row within its partition, with ties receiving the same rank. The rank starts at 1 for the first row in each partition.", "function", { function: {
4831
4831
  inputTypes: ["ANY"],
4832
4832
  outputType: "INT",
4833
4833
  outputPurpose: "PROPERTY",
4834
4834
  example: "rank repo.license by license_repo_count desc;"
4835
4835
  } }),
4836
- new U("LAG", "Accesses data from a previous row in the same partition without changing the partitioning or ordering of the result set. The offset can be specified to retrieve data from a specific number of rows back.", "function", { function: {
4836
+ new G("LAG", "Accesses data from a previous row in the same partition without changing the partitioning or ordering of the result set. The offset can be specified to retrieve data from a specific number of rows back.", "function", { function: {
4837
4837
  inputTypes: ["ANY", "INT"],
4838
4838
  outputType: "ANY",
4839
4839
  outputPurpose: "PROPERTY",
4840
4840
  example: "lag repo.license by license_repo_count desc;"
4841
4841
  } }),
4842
- new U("LEAD", "Accesses data from a subsequent row in the same partition without changing the partitioning or ordering of the result set. The offset can be specified to retrieve data from a specific number of rows ahead.", "function", { function: {
4842
+ new G("LEAD", "Accesses data from a subsequent row in the same partition without changing the partitioning or ordering of the result set. The offset can be specified to retrieve data from a specific number of rows ahead.", "function", { function: {
4843
4843
  inputTypes: ["ANY", "INT"],
4844
4844
  outputType: "ANY",
4845
4845
  outputPurpose: "PROPERTY",
4846
4846
  example: "lead repo.license by license_repo_count desc;"
4847
4847
  } })
4848
- ]), jn = new W("Modeling", [
4849
- new U("What is a Model?", "Models are the heart of Trilogy's semantic layer. They provide a structured, logical representation of data that can evolve independently of the underlying physical model. A model ist just a collection of Trilogy .preql files available for import. In Trilogy Studio, these are editors that have been marked as `sources`. These sources define the concepts, properties, and data retrieval logic for your domain."),
4850
- new U("Purpose and Benefits", "Models enable several key benefits:\n <ul>\n <li><strong>Abstraction:</strong> Define data logically using concepts (like 'customer' or 'order') independent of physical table names or structures.</li>\n <li><strong>Reusability:</strong> Define concepts and logic once in a model source and import them into any number of queries, reports, or other models.</li>\n <li><strong>Maintainability:</strong> If your database schema changes (e.g., table or column rename), you only need to update the corresponding datasource mapping in the model. Queries relying on the logical concepts remain unchanged.</li>\n <li><strong>Consistency:</strong> Ensures everyone querying the data uses the same definitions, metrics, and relationships.</li>\n <li><strong>Governance:</strong> Provides a centralized place to manage data definitions, access rules (via datasources), and metadata (types, traits).</li>\n </ul>", null, null),
4851
- new U("Core Components", "Models have two core components: Concepts and Datasources. These are defined within Trilogy editors marked as model sources.", "section"),
4852
- new U("Concepts", "Concepts keys represent the core entities or logical keys in your data model (e.g., `customer.id`, `product.sku`, `order.id`). Each concept typically has a unique key that identifies individual instances (like `customer.id`). Concepts form the nouns of your data language. They are defined implicitly through properties and datasources associated with them.", "subsection"),
4853
- new U("Properties", "Concept properties are dependent concepts or characteristics associated with a key (e.g., `customer.name`, `order.order_date`, `product.price`). They define context/attributes of keys.\n Properties have defined data types (like `string`, `number`, `datetime`) and can be further annotated with metadata traits (like `::usd` or `::us_state_short`).\n New properties can be added to existing concepts using the `property` keyword:", "subsection"),
4854
- new U("Properties Example", "property order.customer.nation.region.id.headquarters string;\n// This adds a 'headquarters' property of type string\n// under the key `order.customer.nation.region.id.`\nproperty <order.customer.nation.region.id>.headquarters string;\n// this does the same, but with explicit syntax\n// that can be useful when you have a property of multiple keys\nproperty <order.customer.nation.region.id,date.year>.point_in_time_hq string;\n", "code"),
4855
- new U("Types and Traits", "Trilogy has a core type system (e.g., `string`, `number`, `list<T>`, `struct<...>`) that maps to database types.\n Additionally, a metadata-based trait system allows for richer semantic meaning. Traits are appended using `::`.\n Example: `auto states <- ['NY', 'CA', 'TX']::list<string::us_state_short>;` defines a list where each element is a string representing a US state abbreviation. Traits can influence validation, default visualizations (like maps for geo traits), or downstream system behavior.", "subsection", null),
4856
- new U("Types and Traits Example", "import std.geography; // Import traits like us_state_short\n\n// Define a list of strings with a specific trait\nauto states <- ['NY', 'CA', 'TX']::list<string::us_state_short>;\n\n// Define a price with a currency trait\nauto item_price <- 19.99::number::usd;", "code", null),
4857
- new U("Datasources", "Datasources are the bridge between the logical model of concept keys and properties and database tables. They define *how* and *where* to fetch the data for specific properties.\n A datasource maps logical properties to physical columns or calculations and specifies the source.\n Crucially, they also define the `grain` - the level of detail or the set of keys that uniquely identify a row returned by the datasource.", "subsection", null),
4858
- new U("Datasource Example (Database)", "import lineitem; // Assume lineitem provides base concepts like order.id etc.\n\n// Add a new property first (as shown before)\nproperty order.customer.nation.region.id.headquarters string;\n\n// Define a datasource to populate the new property\ndatasource region_headquarters (\n region_id: order.customer.nation.region.id, // Map logical concept key to source column\n headquarters: order.customer.nation.region.headquarters, // Map logical property to source column\n)\ngrain (order.customer.nation.region.id) // Define the unique key for this data\naddress tbl_headquarters;\n''';", "code", null),
4859
- new U("Datasource (Inline Example Description)", "Datasources can also use raw queries as a source, useful for constants, mappings, or small datasets directly within the model definition.", null, null),
4860
- new U("Datasource Example (Inline Query)", "datasource region_headquarters_inline (\n region_id: order.customer.nation.region.id,\n headquarters: order.customer.nation.region.headquarters,\n)\ngrain (order.customer.nation.region.id)\nquery '''\n-- Using an inline query (syntax might vary based on backend)\nselect 1 as region_id, 'HQ1' as headquarters\nunion all\nselect 2 as region_id, 'HQ2' as headquarters\nunion all\nselect 3 as region_id, 'HQ3' as headquarters\nunion all\nselect 4 as region_id, 'HQ4' as headquarters\n''';", "code", null),
4861
- new U("Using and Managing Models", "How to leverage and maintain your models.", "section", null),
4862
- new U("Imports", "Models are composed using the `import` statement. You can import entire model source editors by their name (or alias if specified in the model config). This makes the concepts, properties, and datasources defined in that source available in the current editor.\n The standard library (`std`) is always available for import, providing common functions and traits.", "subsection", null),
4863
- new U("Imports Example", "// Import concepts/properties defined in the 'lineitem' editor source\nimport lineitem;\n\n// Import geography traits from the standard library\nimport std.geography;\n\n// Import concepts from 'customer_details' editor source, aliasing it to 'cust'\nimport customer_details as cust;\n\n// Now you can use concepts like:\n// lineitem.order.id\n// std.geography.us_state_short\n// cust.customer.address", "code", null),
4864
- new U("Constants", "The simplest form of a concept is a constant, defined using `const`. Constants do not require a datasource as their value is fixed directly in the definition. They are useful for defining fixed values, parameters, or variables within scripts or queries.", "subsection", null),
4865
- new U("Constants Example", "const pi <- 3.14;\nconst company_founding_year <- 2023;\n\nselect pi * 2 as circumference_ratio;", "code", null),
4866
- new U("Creating Model Sources", "Any Trilogy editor can be designated as a source file for a model. This is typically done through the Trilogy UI or configuration. Once an editor is part of a model, its definitions become importable.", "subsection", null),
4867
- new U("Tip: Generating Models", "You can quickly create a basic model source from a database table. When browsing tables in a connection, use the \"Create model from table\" action. This generates an editor with properties mapped to columns and a basic datasource.", "tip", null),
4868
- new U("Model Parsing and Validation", "Models are parsed and validated to ensure correctness and build the metadata necessary for queries and tooling. This typically happens automatically when a query referencing the model is run. You can also trigger parsing manually (e.g., via a 'Parse Model' button in the UI). Parsing checks syntax, type consistency, and resolves imports. Errors found during parsing will be reported, often linking back to the specific editor and line number causing the issue.", "subsection", null),
4869
- new U("Standard Library", "Trilogy includes a standard library, typically available under the `std` namespace (e.g., `import std;` or `import std.math;`). This library provides common functions, operators, and useful metadata traits (like geo-location, currency, etc.).", "subsection", null),
4870
- new U("Tip: Standard Library", "Review the documentation for the standard library (`std`) to discover always-available functions and traits you can use in your models and queries.", "tip", null)
4871
- ], "Modeling"), Mn = new G("Trilogy Reference", [
4872
- new W("Overview/Goals", [
4873
- new U("Introduction", "Trilogy is a new, SQL-derived language that compiles to SQL. Trilogy Studio provides you with both native Trilogy and SQL editors, allowing you to query your data in a way that is more expressive and easier to maintain than traditional SQL."),
4874
- new U("Introduction", "Trilogy aspires to simplify hard parts of SQL, keep and enhance the good, incentivize the best, and add modern reusability, type-checking, and ergonomics. It should be approachable to query by someone that knows only SQL, and easy to model for someone familiar with SQL or Python."),
4875
- new U("Iterative SQL Development", "To capture the iterative SQL development loop, Trilogy directly embeds a lightweight metadata definition syntax, allowing you to query and evolve the semantic layer in the same tool - and even in the same query session. This definition language (semantic layer, ERD, etc.) provides static typing/enforcement, and handles joins [1-1, 1-many, many-to-1], aggregations, filtering, and nulls automatically."),
4876
- new U("Trilogy Query Structure", "Let's take a look. Standard Trilogy reads like SQL SELECT, without the FROM or GROUP BY clauses."),
4877
- new U("Trilogy Query Example", "WHERE\n store_sales.date.year=2001 \n AND store_sales.date.month_of_year=1 \n AND store_sales.item.current_price > 1.2 * avg(store_sales.item.current_price) BY store_sales.item.category\nSELECT\n store_sales.customer.state,\n count(store_sales.customer.id) AS customer_count\nHAVING\n customer_count > 10\nORDER BY\n customer_count ASC NULLS FIRST,\n store_sales.customer.state ASC NULLS FIRST\nLIMIT 100;", "code"),
4878
- new U("How Trilogy Differs from SQL", "Those aren't the only clauses to be gone - Trilogy removes the need for joins - and even tables - via the metadata layer. Information about how to join tables is encoded once and used automatically if required. Definitions look like creating a SQL table, but columns are bound to an additionally abstracted concept label, not raw datatypes. This enables Trilogy to dynamically traverse joins when needed."),
4879
- new U("How Trilogy Differs from SQL", "key order_id int;\nkey store_id int;\nproperty store_id.store_name string;\n\ndatasource orders (\n order_id: order_id,\n store_id: store_id\n)\ngrain (order_id)\naddress tbl_orders;\n\ndatasource stores (\n store_id: store_id\n store_name: store_name\n)\ngrain (store_id)\naddress tbl_stores;\n", "code"),
4880
- new U("The Value of Trilogy", "But writing out that datasource is work - a table can be queried quickly because someone else set it up. Trilogy provides the same experience through model imports. It's expected that datasources will be written once, and reused many times. Trilogy can be shared and imported through .preql files using a python-like import syntax."),
4881
- new U("The Value of Trilogy", "Complex models spanning dozens of tables can be imported with a single line."),
4882
- new U("Trilogy Equivalent", "import concepts.sales AS sales;\nSELECT\n sales.territory_name,\n sales.sub_total,\n sales.total_taxes,\n sales.total_sales\nORDER BY\n sales.territory_name DESC;", "code"),
4883
- new U("How Trilogy Works", "Explore the rest of the reference documentation for more details on how Trilogy works and explore these concepts in more detail.")
4848
+ ]), jn = new K("Modeling", [
4849
+ new G("What is a Model?", "Models are the heart of Trilogy's semantic layer. They provide a structured, logical representation of data that can evolve independently of the underlying physical model. A model ist just a collection of Trilogy .preql files available for import. In Trilogy Studio, these are editors that have been marked as `sources`. These sources define the concepts, properties, and data retrieval logic for your domain."),
4850
+ new G("Purpose and Benefits", "Models enable several key benefits:\n <ul>\n <li><strong>Abstraction:</strong> Define data logically using concepts (like 'customer' or 'order') independent of physical table names or structures.</li>\n <li><strong>Reusability:</strong> Define concepts and logic once in a model source and import them into any number of queries, reports, or other models.</li>\n <li><strong>Maintainability:</strong> If your database schema changes (e.g., table or column rename), you only need to update the corresponding datasource mapping in the model. Queries relying on the logical concepts remain unchanged.</li>\n <li><strong>Consistency:</strong> Ensures everyone querying the data uses the same definitions, metrics, and relationships.</li>\n <li><strong>Governance:</strong> Provides a centralized place to manage data definitions, access rules (via datasources), and metadata (types, traits).</li>\n </ul>", null, null),
4851
+ new G("Core Components", "Models have two core components: Concepts and Datasources. These are defined within Trilogy editors marked as model sources.", "section"),
4852
+ new G("Concepts", "Concepts keys represent the core entities or logical keys in your data model (e.g., `customer.id`, `product.sku`, `order.id`). Each concept typically has a unique key that identifies individual instances (like `customer.id`). Concepts form the nouns of your data language. They are defined implicitly through properties and datasources associated with them.", "subsection"),
4853
+ new G("Properties", "Concept properties are dependent concepts or characteristics associated with a key (e.g., `customer.name`, `order.order_date`, `product.price`). They define context/attributes of keys.\n Properties have defined data types (like `string`, `number`, `datetime`) and can be further annotated with metadata traits (like `::usd` or `::us_state_short`).\n New properties can be added to existing concepts using the `property` keyword:", "subsection"),
4854
+ new G("Properties Example", "property order.customer.nation.region.id.headquarters string;\n// This adds a 'headquarters' property of type string\n// under the key `order.customer.nation.region.id.`\nproperty <order.customer.nation.region.id>.headquarters string;\n// this does the same, but with explicit syntax\n// that can be useful when you have a property of multiple keys\nproperty <order.customer.nation.region.id,date.year>.point_in_time_hq string;\n", "code"),
4855
+ new G("Types and Traits", "Trilogy has a core type system (e.g., `string`, `number`, `list<T>`, `struct<...>`) that maps to database types.\n Additionally, a metadata-based trait system allows for richer semantic meaning. Traits are appended using `::`.\n Example: `auto states <- ['NY', 'CA', 'TX']::list<string::us_state_short>;` defines a list where each element is a string representing a US state abbreviation. Traits can influence validation, default visualizations (like maps for geo traits), or downstream system behavior.", "subsection", null),
4856
+ new G("Types and Traits Example", "import std.geography; // Import traits like us_state_short\n\n// Define a list of strings with a specific trait\nauto states <- ['NY', 'CA', 'TX']::list<string::us_state_short>;\n\n// Define a price with a currency trait\nauto item_price <- 19.99::number::usd;", "code", null),
4857
+ new G("Datasources", "Datasources are the bridge between the logical model of concept keys and properties and database tables. They define *how* and *where* to fetch the data for specific properties.\n A datasource maps logical properties to physical columns or calculations and specifies the source.\n Crucially, they also define the `grain` - the level of detail or the set of keys that uniquely identify a row returned by the datasource.", "subsection", null),
4858
+ new G("Datasource Example (Database)", "import lineitem; // Assume lineitem provides base concepts like order.id etc.\n\n// Add a new property first (as shown before)\nproperty order.customer.nation.region.id.headquarters string;\n\n// Define a datasource to populate the new property\ndatasource region_headquarters (\n region_id: order.customer.nation.region.id, // Map logical concept key to source column\n headquarters: order.customer.nation.region.headquarters, // Map logical property to source column\n)\ngrain (order.customer.nation.region.id) // Define the unique key for this data\naddress tbl_headquarters;\n''';", "code", null),
4859
+ new G("Datasource (Inline Example Description)", "Datasources can also use raw queries as a source, useful for constants, mappings, or small datasets directly within the model definition.", null, null),
4860
+ new G("Datasource Example (Inline Query)", "datasource region_headquarters_inline (\n region_id: order.customer.nation.region.id,\n headquarters: order.customer.nation.region.headquarters,\n)\ngrain (order.customer.nation.region.id)\nquery '''\n-- Using an inline query (syntax might vary based on backend)\nselect 1 as region_id, 'HQ1' as headquarters\nunion all\nselect 2 as region_id, 'HQ2' as headquarters\nunion all\nselect 3 as region_id, 'HQ3' as headquarters\nunion all\nselect 4 as region_id, 'HQ4' as headquarters\n''';", "code", null),
4861
+ new G("Using and Managing Models", "How to leverage and maintain your models.", "section", null),
4862
+ new G("Imports", "Models are composed using the `import` statement. You can import entire model source editors by their name (or alias if specified in the model config). This makes the concepts, properties, and datasources defined in that source available in the current editor.\n The standard library (`std`) is always available for import, providing common functions and traits.", "subsection", null),
4863
+ new G("Imports Example", "// Import concepts/properties defined in the 'lineitem' editor source\nimport lineitem;\n\n// Import geography traits from the standard library\nimport std.geography;\n\n// Import concepts from 'customer_details' editor source, aliasing it to 'cust'\nimport customer_details as cust;\n\n// Now you can use concepts like:\n// lineitem.order.id\n// std.geography.us_state_short\n// cust.customer.address", "code", null),
4864
+ new G("Constants", "The simplest form of a concept is a constant, defined using `const`. Constants do not require a datasource as their value is fixed directly in the definition. They are useful for defining fixed values, parameters, or variables within scripts or queries.", "subsection", null),
4865
+ new G("Constants Example", "const pi <- 3.14;\nconst company_founding_year <- 2023;\n\nselect pi * 2 as circumference_ratio;", "code", null),
4866
+ new G("Creating Model Sources", "Any Trilogy editor can be designated as a source file for a model. This is typically done through the Trilogy UI or configuration. Once an editor is part of a model, its definitions become importable.", "subsection", null),
4867
+ new G("Tip: Generating Models", "You can quickly create a basic model source from a database table. When browsing tables in a connection, use the \"Create model from table\" action. This generates an editor with properties mapped to columns and a basic datasource.", "tip", null),
4868
+ new G("Model Parsing and Validation", "Models are parsed and validated to ensure correctness and build the metadata necessary for queries and tooling. This typically happens automatically when a query referencing the model is run. You can also trigger parsing manually (e.g., via a 'Parse Model' button in the UI). Parsing checks syntax, type consistency, and resolves imports. Errors found during parsing will be reported, often linking back to the specific editor and line number causing the issue.", "subsection", null),
4869
+ new G("Standard Library", "Trilogy includes a standard library, typically available under the `std` namespace (e.g., `import std;` or `import std.math;`). This library provides common functions, operators, and useful metadata traits (like geo-location, currency, etc.).", "subsection", null),
4870
+ new G("Tip: Standard Library", "Review the documentation for the standard library (`std`) to discover always-available functions and traits you can use in your models and queries.", "tip", null)
4871
+ ], "Modeling"), Mn = new q("Trilogy Reference", [
4872
+ new K("Overview/Goals", [
4873
+ new G("Introduction", "Trilogy is a new, SQL-derived language that compiles to SQL. Trilogy Studio provides you with both native Trilogy and SQL editors, allowing you to query your data in a way that is more expressive and easier to maintain than traditional SQL."),
4874
+ new G("Introduction", "Trilogy aspires to simplify hard parts of SQL, keep and enhance the good, incentivize the best, and add modern reusability, type-checking, and ergonomics. It should be approachable to query by someone that knows only SQL, and easy to model for someone familiar with SQL or Python."),
4875
+ new G("Iterative SQL Development", "To capture the iterative SQL development loop, Trilogy directly embeds a lightweight metadata definition syntax, allowing you to query and evolve the semantic layer in the same tool - and even in the same query session. This definition language (semantic layer, ERD, etc.) provides static typing/enforcement, and handles joins [1-1, 1-many, many-to-1], aggregations, filtering, and nulls automatically."),
4876
+ new G("Trilogy Query Structure", "Let's take a look. Standard Trilogy reads like SQL SELECT, without the FROM or GROUP BY clauses."),
4877
+ new G("Trilogy Query Example", "WHERE\n store_sales.date.year=2001 \n AND store_sales.date.month_of_year=1 \n AND store_sales.item.current_price > 1.2 * avg(store_sales.item.current_price) BY store_sales.item.category\nSELECT\n store_sales.customer.state,\n count(store_sales.customer.id) AS customer_count\nHAVING\n customer_count > 10\nORDER BY\n customer_count ASC NULLS FIRST,\n store_sales.customer.state ASC NULLS FIRST\nLIMIT 100;", "code"),
4878
+ new G("How Trilogy Differs from SQL", "Those aren't the only clauses to be gone - Trilogy removes the need for joins - and even tables - via the metadata layer. Information about how to join tables is encoded once and used automatically if required. Definitions look like creating a SQL table, but columns are bound to an additionally abstracted concept label, not raw datatypes. This enables Trilogy to dynamically traverse joins when needed."),
4879
+ new G("How Trilogy Differs from SQL", "key order_id int;\nkey store_id int;\nproperty store_id.store_name string;\n\ndatasource orders (\n order_id: order_id,\n store_id: store_id\n)\ngrain (order_id)\naddress tbl_orders;\n\ndatasource stores (\n store_id: store_id\n store_name: store_name\n)\ngrain (store_id)\naddress tbl_stores;\n", "code"),
4880
+ new G("The Value of Trilogy", "But writing out that datasource is work - a table can be queried quickly because someone else set it up. Trilogy provides the same experience through model imports. It's expected that datasources will be written once, and reused many times. Trilogy can be shared and imported through .preql files using a python-like import syntax."),
4881
+ new G("The Value of Trilogy", "Complex models spanning dozens of tables can be imported with a single line."),
4882
+ new G("Trilogy Equivalent", "import concepts.sales AS sales;\nSELECT\n sales.territory_name,\n sales.sub_total,\n sales.total_taxes,\n sales.total_sales\nORDER BY\n sales.territory_name DESC;", "code"),
4883
+ new G("How Trilogy Works", "Explore the rest of the reference documentation for more details on how Trilogy works and explore these concepts in more detail.")
4884
4884
  ]),
4885
- new W("Code", [new U("How Trilogy Works", "Studio and dependent code is open-source under the MIT license. The code is available on GitHub at the following locations:\n <li><a href='https://github.com/trilogy-data/trilogy-studio-core' target='_blank'>trilogy-studio-core</a> - the core code for the studio, including the language server and query editor.</li>\n <li><a href='https://github.com/trilogy-data/pytrilogy' target='_blank'>pytrilogy</a> - reference implementation of Trilogy language parsing/execution.</li>"), new U("How Trilogy Works", "Stars appreciated, contributions welcome! If you find a bug, please file an issue on the relevant repository.\n ")]),
4886
- new W("Querying Data", [
4887
- new U("SELECT Without FROM, JOIN, or GROUP BY", "In Trilogy, you write queries without explicitly specifying tables, joins, or grouping."),
4888
- new U("Example Query", "SELECT\n product_line,\n revenue,\n revenue_from_top_customers,\n revenue_from_top_customers / revenue AS revenue_from_top_customers_pct\nWHERE\n product_line IN ('widgets', 'doodads');", "code"),
4889
- new U("SELECT Without FROM, JOIN, or GROUP BY", "Queries begin with a SELECT, and can include WHERE, HAVING, ORDER BY, and LIMIT clauses. Trilogy automatically handles joins and grouping based on the defined model."),
4890
- new U("SELECT Without FROM, JOIN, or GROUP BY", "Like SQL, the where clause filters results *before* the selection, and the having *after*. The order by clause sorts the results, and the limit clause restricts the number of rows returned."),
4891
- new U("SELECT Without FROM, JOIN, or GROUP BY", "This query would restrict data to red products in Massachsusetts, then select the product name and aggregate total sales within the filter state, further filter to only those with total sales greater than 1000, ordering by total sales in descending order, and limiting the results to 10 rows."),
4892
- new U("Example Query", "WHERE product_name like '%red%' and sales.state = 'MA' \nSELECT \n product_name, \n sum(sales) AS total_sales \nHAVING total_sales > 1000 \nORDER BY \n total_sales DESC \nLIMIT 10;", "code"),
4893
- new U("SELECT Without FROM, JOIN, or GROUP BY", "All concepts and outputs must be explicitly named, using AS to bind any transformation. The having clause and order clause can only reference fields in the select list, while the where clause can reference any field in the model.")
4885
+ new K("Code", [new G("How Trilogy Works", "Studio and dependent code is open-source under the MIT license. The code is available on GitHub at the following locations:\n <li><a href='https://github.com/trilogy-data/trilogy-studio-core' target='_blank'>trilogy-studio-core</a> - the core code for the studio, including the language server and query editor.</li>\n <li><a href='https://github.com/trilogy-data/pytrilogy' target='_blank'>pytrilogy</a> - reference implementation of Trilogy language parsing/execution.</li>"), new G("How Trilogy Works", "Stars appreciated, contributions welcome! If you find a bug, please file an issue on the relevant repository.\n ")]),
4886
+ new K("Querying Data", [
4887
+ new G("SELECT Without FROM, JOIN, or GROUP BY", "In Trilogy, you write queries without explicitly specifying tables, joins, or grouping."),
4888
+ new G("Example Query", "SELECT\n product_line,\n revenue,\n revenue_from_top_customers,\n revenue_from_top_customers / revenue AS revenue_from_top_customers_pct\nWHERE\n product_line IN ('widgets', 'doodads');", "code"),
4889
+ new G("SELECT Without FROM, JOIN, or GROUP BY", "Queries begin with a SELECT, and can include WHERE, HAVING, ORDER BY, and LIMIT clauses. Trilogy automatically handles joins and grouping based on the defined model."),
4890
+ new G("SELECT Without FROM, JOIN, or GROUP BY", "Like SQL, the where clause filters results *before* the selection, and the having *after*. The order by clause sorts the results, and the limit clause restricts the number of rows returned."),
4891
+ new G("SELECT Without FROM, JOIN, or GROUP BY", "This query would restrict data to red products in Massachsusetts, then select the product name and aggregate total sales within the filter state, further filter to only those with total sales greater than 1000, ordering by total sales in descending order, and limiting the results to 10 rows."),
4892
+ new G("Example Query", "WHERE product_name like '%red%' and sales.state = 'MA' \nSELECT \n product_name, \n sum(sales) AS total_sales \nHAVING total_sales > 1000 \nORDER BY \n total_sales DESC \nLIMIT 10;", "code"),
4893
+ new G("SELECT Without FROM, JOIN, or GROUP BY", "All concepts and outputs must be explicitly named, using AS to bind any transformation. The having clause and order clause can only reference fields in the select list, while the where clause can reference any field in the model.")
4894
4894
  ]),
4895
- new W("Syntax", [
4896
- new U("Trilogy Syntax", "Trilogy scripts contain semantic declarations and queries. Statements end with a semicolon, comments begin with #, and only SELECT statements return rows. Trilogy resembles SQL, but concepts replace table columns and the semantic model resolves datasources and joins."),
4897
- new U("Basic SELECT", "where orders.status = 'complete'\nselect\n orders.customer.name,\n sum(orders.revenue) as total_revenue\nhaving sum(orders.revenue) > 1000\norder by sum(orders.revenue) desc\nlimit 100;", "code"),
4898
- new U("SELECT Structure", "WHERE filters source rows before aggregation or window evaluation. SELECT defines the output and its grain. HAVING filters the projected result, including aggregates and windows. ORDER BY and LIMIT are applied last. WHERE is conventionally written before SELECT."),
4899
- new U("SELECT Structure", "with <name> as?\nwhere <condition>?\nselect\n <expression> [as <alias>], ...\n <subset|union join clauses>?\nhaving <condition>?\norder by <expression> [asc|desc]?\nlimit <count>?;", "code"),
4900
- new U("Fields and Aliases", "Use the full path for imported concepts, such as orders.customer.name. Existing concepts can be selected directly. Every new expression should be named with AS. An output alias is a label, not a reusable concept: do not reference it inside calculations or WHERE, HAVING, or ORDER BY. Define an AUTO or METRIC when an expression must be reused."),
4901
- new U("Aliases", "select\n orders.customer.id,\n sum(orders.revenue) as total_revenue,\n sum(orders.revenue) / count(orders.id) as average_order_value\norder by sum(orders.revenue) desc;", "code"),
4902
- new U("Automatic Grouping", "Trilogy has no GROUP BY clause. Aggregates automatically group to the non-aggregated dimensions selected by the query. Override one aggregate with BY, use BY * for a grand total, or place BY ROLLUP, BY CUBE, or BY GROUPING SETS after the select list for multi-level output."),
4903
- new U("Explicit Aggregate Grain", "select\n orders.customer.region,\n sum(orders.revenue) as regional_revenue,\n sum(orders.revenue) by * as company_revenue,\n sum(orders.revenue) / (sum(orders.revenue) by *) as revenue_share;", "code"),
4904
- new U("Inline Filters", "Use expression ? condition to filter the input to one expression without filtering the whole query. Parenthesize arithmetic on the left side. This is especially useful for conditional aggregates."),
4905
- new U("Inline Filter", "select\n orders.customer.region,\n sum(orders.revenue) as all_revenue,\n sum(orders.revenue ? orders.status = 'complete') as completed_revenue;", "code"),
4906
- new U("Imports", "IMPORT makes a reusable model available under an alias. Imported relationships are exposed through dot paths, so importing a fact model normally also provides access to its dimensions."),
4907
- new U("Import", "import orders as orders;", "code"),
4908
- new U("Parameters", "PARAMETER declares a runtime value. A parameter without a default must be supplied by the caller; a default makes it optional."),
4909
- new U("Parameter", "parameter minimum_revenue numeric default 1000;\n\nwhere orders.revenue >= minimum_revenue\nselect orders.id, orders.revenue;", "code"),
4910
- new U("Named Rowsets", "Prefix a SELECT with WITH name AS to create a reusable rowset. Its outputs are namespaced beneath that name and can be referenced by later statements."),
4911
- new U("Named Rowset", "with large_orders as\nwhere orders.revenue > 1000\nselect\n orders.id,\n orders.customer.id,\n orders.revenue;\n\nselect\n large_orders.customer.id,\n sum(large_orders.revenue) as total_revenue;", "code"),
4912
- new U("Scoped Joins", "Use SUBSET JOIN or UNION JOIN inside a SELECT when concepts from separate models share a value domain. SUBSET declares that the left domain is contained by the right; UNION preserves values exclusive to either side. Joins declare relationships and do not imply row filtering—use explicit predicates to restrict rows. Join every key in a composite grain."),
4913
- new U("Scoped Join", "select\n actuals.account_id,\n sum(actuals.amount) as actual_amount,\n sum(budget.amount) as budget_amount\nunion join actuals.account_id = budget.account_id;", "code"),
4914
- new U("Expressions", "Cast with ::type, call every function with parentheses, and use unquoted date parts such as date_part(orders.created_at, year). Logical AND binds more tightly than OR. The inline-filter operator ? binds to a primary expression, so write (revenue - cost) ? condition when filtering arithmetic."),
4915
- new U("How Trilogy Differs from SQL", "Do not write FROM, GROUP BY, DISTINCT, SELECT *, SQL subqueries, or SQL-style set operators. Use model paths instead of FROM/JOIN, automatic grain instead of GROUP BY, count_distinct for non-key distinct counts, named rowsets instead of subqueries, and union(...) to stack rows. The -- prefix hides a selected field; it is not a comment.", "warning"),
4916
- new U("Reusable Definitions", "KEY, PROPERTY, AUTO, and METRIC declare reusable concepts; their expressions are evaluated in the scope of the query that references them. DEF declares a reusable expression macro invoked with @name(...). See Concepts and Datasources and Joins for full modeling syntax."),
4917
- new U("Definition Examples", "key customer_id int;\nproperty customer_id.customer_name string;\nauto gross_profit <- revenue - cost;\nmetric total_revenue <- sum(revenue);\ndef percent(part, whole) -> 100 * part / whole;", "code")
4895
+ new K("Syntax", [
4896
+ new G("Trilogy Syntax", "Trilogy scripts contain semantic declarations and queries. Statements end with a semicolon, comments begin with #, and only SELECT statements return rows. Trilogy resembles SQL, but concepts replace table columns and the semantic model resolves datasources and joins."),
4897
+ new G("Basic SELECT", "where orders.status = 'complete'\nselect\n orders.customer.name,\n sum(orders.revenue) as total_revenue\nhaving sum(orders.revenue) > 1000\norder by sum(orders.revenue) desc\nlimit 100;", "code"),
4898
+ new G("SELECT Structure", "WHERE filters source rows before aggregation or window evaluation. SELECT defines the output and its grain. HAVING filters the projected result, including aggregates and windows. ORDER BY and LIMIT are applied last. WHERE is conventionally written before SELECT."),
4899
+ new G("SELECT Structure", "with <name> as?\nwhere <condition>?\nselect\n <expression> [as <alias>], ...\n <subset|union join clauses>?\nhaving <condition>?\norder by <expression> [asc|desc]?\nlimit <count>?;", "code"),
4900
+ new G("Fields and Aliases", "Use the full path for imported concepts, such as orders.customer.name. Existing concepts can be selected directly. Every new expression should be named with AS. An output alias is a label, not a reusable concept: do not reference it inside calculations or WHERE, HAVING, or ORDER BY. Define an AUTO or METRIC when an expression must be reused."),
4901
+ new G("Aliases", "select\n orders.customer.id,\n sum(orders.revenue) as total_revenue,\n sum(orders.revenue) / count(orders.id) as average_order_value\norder by sum(orders.revenue) desc;", "code"),
4902
+ new G("Automatic Grouping", "Trilogy has no GROUP BY clause. Aggregates automatically group to the non-aggregated dimensions selected by the query. Override one aggregate with BY, use BY * for a grand total, or place BY ROLLUP, BY CUBE, or BY GROUPING SETS after the select list for multi-level output."),
4903
+ new G("Explicit Aggregate Grain", "select\n orders.customer.region,\n sum(orders.revenue) as regional_revenue,\n sum(orders.revenue) by * as company_revenue,\n sum(orders.revenue) / (sum(orders.revenue) by *) as revenue_share;", "code"),
4904
+ new G("Inline Filters", "Use expression ? condition to filter the input to one expression without filtering the whole query. Parenthesize arithmetic on the left side. This is especially useful for conditional aggregates."),
4905
+ new G("Inline Filter", "select\n orders.customer.region,\n sum(orders.revenue) as all_revenue,\n sum(orders.revenue ? orders.status = 'complete') as completed_revenue;", "code"),
4906
+ new G("Imports", "IMPORT makes a reusable model available under an alias. Imported relationships are exposed through dot paths, so importing a fact model normally also provides access to its dimensions."),
4907
+ new G("Import", "import orders as orders;", "code"),
4908
+ new G("Parameters", "PARAMETER declares a runtime value. A parameter without a default must be supplied by the caller; a default makes it optional."),
4909
+ new G("Parameter", "parameter minimum_revenue numeric default 1000;\n\nwhere orders.revenue >= minimum_revenue\nselect orders.id, orders.revenue;", "code"),
4910
+ new G("Named Rowsets", "Prefix a SELECT with WITH name AS to create a reusable rowset. Its outputs are namespaced beneath that name and can be referenced by later statements."),
4911
+ new G("Named Rowset", "with large_orders as\nwhere orders.revenue > 1000\nselect\n orders.id,\n orders.customer.id,\n orders.revenue;\n\nselect\n large_orders.customer.id,\n sum(large_orders.revenue) as total_revenue;", "code"),
4912
+ new G("Scoped Joins", "Use SUBSET JOIN or UNION JOIN inside a SELECT when concepts from separate models share a value domain. SUBSET declares that the left domain is contained by the right; UNION preserves values exclusive to either side. Joins declare relationships and do not imply row filtering—use explicit predicates to restrict rows. Join every key in a composite grain."),
4913
+ new G("Scoped Join", "select\n actuals.account_id,\n sum(actuals.amount) as actual_amount,\n sum(budget.amount) as budget_amount\nunion join actuals.account_id = budget.account_id;", "code"),
4914
+ new G("Expressions", "Cast with ::type, call every function with parentheses, and use unquoted date parts such as date_part(orders.created_at, year). Logical AND binds more tightly than OR. The inline-filter operator ? binds to a primary expression, so write (revenue - cost) ? condition when filtering arithmetic."),
4915
+ new G("How Trilogy Differs from SQL", "Do not write FROM, GROUP BY, DISTINCT, SELECT *, SQL subqueries, or SQL-style set operators. Use model paths instead of FROM/JOIN, automatic grain instead of GROUP BY, count_distinct for non-key distinct counts, named rowsets instead of subqueries, and union(...) to stack rows. The -- prefix hides a selected field; it is not a comment.", "warning"),
4916
+ new G("Reusable Definitions", "KEY, PROPERTY, AUTO, and METRIC declare reusable concepts; their expressions are evaluated in the scope of the query that references them. DEF declares a reusable expression macro invoked with @name(...). See Concepts and Datasources and Joins for full modeling syntax."),
4917
+ new G("Definition Examples", "key customer_id int;\nproperty customer_id.customer_name string;\nauto gross_profit <- revenue - cost;\nmetric total_revenue <- sum(revenue);\ndef percent(part, whole) -> 100 * part / whole;", "code")
4918
4918
  ]),
4919
- new W("Concepts", [
4920
- new U("What Are Concepts?", "Concepts are core semantic building blocks in Trilogy. They represent keys, properties, or metrics: Keys (unique identifiers), Properties (additional values), and Metrics (aggregatable values)."),
4921
- new U("Example", "key product_id int;# the unique identifier of a product \nproperty product_id.product_name string;# the name of a product \nmetric product_count <-count(product.id); # the count of products", "code"),
4922
- new U("Keys", "Keys represent a unique conceptual grain. A combination of one or more keys will uniquely identify a set of properties. If familiar with databases, think of them as your primary/foreign keys. More generally, you can thing of them as something like a passport number, a product ID, a URL, or a stock ticker - a unique shorthand for an entity."),
4923
- new U("Key Syntax", "Keys have straight forward syntax; the keyword, the name, and the type."),
4924
- new U("Example", "key product_id int;# the unique identifier of a product", "code"),
4925
- new U("Properties", "Properties are values associated with one or more keys"),
4926
- new U("Property Syntax", "Properties have a richer syntax than keys - they require the associated keys, within <>, before the property name.. syntax: `\"property\" <key1, key2, ..> \".\" IDENTIFIER type;`"),
4927
- new U("Single Key Property", "A property can be associated with a single key with a shorthand syntax without the full <> group. syntax: `\"property\" key \".\" IDENTIFIER type;`", "tip"),
4928
- new U("code", "property product_id.product_name string;# the name of a product", "code"),
4929
- new U("Metrics", "Metrics are aggregatable values, and can come from properties, keys, or other metrics. Like keys, they have a grain, though this is inferred from the aggregation"),
4930
- new U("Example", "metric product_count <-count(product.id); # the count of products", "code"),
4931
- new U("Dynamic Grain", "A basic metric - like `metric product_count <- count(product_id);` - will have a dynamic aggregation level that corresponds to the query it is used in."),
4932
- new U("Static Grain", "A metric can be created by an aggregation with a defined grain, ex: `metric product_count <-count(product_id) by store;`, in which case it behaves similar to a property. However, where conditions for a select will still be pushed inside these aggregates.")
4919
+ new K("Concepts", [
4920
+ new G("What Are Concepts?", "Concepts are core semantic building blocks in Trilogy. They represent keys, properties, or metrics: Keys (unique identifiers), Properties (additional values), and Metrics (aggregatable values)."),
4921
+ new G("Example", "key product_id int;# the unique identifier of a product \nproperty product_id.product_name string;# the name of a product \nmetric product_count <-count(product.id); # the count of products", "code"),
4922
+ new G("Keys", "Keys represent a unique conceptual grain. A combination of one or more keys will uniquely identify a set of properties. If familiar with databases, think of them as your primary/foreign keys. More generally, you can thing of them as something like a passport number, a product ID, a URL, or a stock ticker - a unique shorthand for an entity."),
4923
+ new G("Key Syntax", "Keys have straight forward syntax; the keyword, the name, and the type."),
4924
+ new G("Example", "key product_id int;# the unique identifier of a product", "code"),
4925
+ new G("Properties", "Properties are values associated with one or more keys"),
4926
+ new G("Property Syntax", "Properties have a richer syntax than keys - they require the associated keys, within <>, before the property name.. syntax: `\"property\" <key1, key2, ..> \".\" IDENTIFIER type;`"),
4927
+ new G("Single Key Property", "A property can be associated with a single key with a shorthand syntax without the full <> group. syntax: `\"property\" key \".\" IDENTIFIER type;`", "tip"),
4928
+ new G("code", "property product_id.product_name string;# the name of a product", "code"),
4929
+ new G("Metrics", "Metrics are aggregatable values, and can come from properties, keys, or other metrics. Like keys, they have a grain, though this is inferred from the aggregation"),
4930
+ new G("Example", "metric product_count <-count(product.id); # the count of products", "code"),
4931
+ new G("Dynamic Grain", "A basic metric - like `metric product_count <- count(product_id);` - will have a dynamic aggregation level that corresponds to the query it is used in."),
4932
+ new G("Static Grain", "A metric can be created by an aggregation with a defined grain, ex: `metric product_count <-count(product_id) by store;`, in which case it behaves similar to a property. However, where conditions for a select will still be pushed inside these aggregates.")
4933
4933
  ]),
4934
- new W("Datasources and Joins", [
4935
- new U("Defining Datasources", "A table in a warehouse is defined as a dataset with each column bound to a concept. When datasources share a common key, trilogy can join between them. Not all joins are equal; some are 1-1, 1-many, or many-1. Trilogy handles these by looking at the grain of each datasource involved. If a join is on a key other than the grain of the sources, the output may need to be grouped up to avoid duplication. "),
4936
- new U("Partial Keys", "A table may not have all values of a key. For example, a list of orders may not have every customer ID, as not all customers may have placed orders. A datasource can be defined with a key marked as partial by prefixing the ~ character. Trilogy will ensure that partial sources are used on the right side of an outer join to avoid implicitly reducing results."),
4937
- new U("Example", "datasource sales (\n order_id: orders.id,\n cu_id: ~customers.id,\n revenue: orders.revenue\n)\ngrain (orders.id)\naddress warehouse.sales;\n\ndatasource customers (\n customer_id: customers.id,\n name: customers.name\n)\ngrain (customers.id)\naddress warehouse.customers;", "code"),
4938
- new U("Partial Data", "Sometimes partial data is all we need. For example, imagine a recent and archive table. When you query the last 7 days, and the last 30 are in recent - we can prune the archive."),
4939
- new U("Example", "partial datasource recent_sales (\n order_id: orders.id,\n cu_id: customers.id,\n revenue: orders.revenue,\n date: orders.date\n)\ngrain (orders.id)\ncomplete where orders.date > current_date - 30\naddress warehouse.sales;\n\npartial datasource archive_sales (\n order_id: orders.id,\n cu_id: customers.id,\n revenue: orders.revenue,\n date: orders.date\n)\ngrain (orders.id)\ncomplete where orders.date <= current_date - 30\naddress warehouse.sales_archive;", "code"),
4940
- new U("Partial Keys", "A query of the form `where orders.date > current_date -7 can safely resolve to just our recent. This lets us efficiently prune and select relevant datasources.")
4934
+ new K("Datasources and Joins", [
4935
+ new G("Defining Datasources", "A table in a warehouse is defined as a dataset with each column bound to a concept. When datasources share a common key, trilogy can join between them. Not all joins are equal; some are 1-1, 1-many, or many-1. Trilogy handles these by looking at the grain of each datasource involved. If a join is on a key other than the grain of the sources, the output may need to be grouped up to avoid duplication. "),
4936
+ new G("Partial Keys", "A table may not have all values of a key. For example, a list of orders may not have every customer ID, as not all customers may have placed orders. A datasource can be defined with a key marked as partial by prefixing the ~ character. Trilogy will ensure that partial sources are used on the right side of an outer join to avoid implicitly reducing results."),
4937
+ new G("Example", "datasource sales (\n order_id: orders.id,\n cu_id: ~customers.id,\n revenue: orders.revenue\n)\ngrain (orders.id)\naddress warehouse.sales;\n\ndatasource customers (\n customer_id: customers.id,\n name: customers.name\n)\ngrain (customers.id)\naddress warehouse.customers;", "code"),
4938
+ new G("Partial Data", "Sometimes partial data is all we need. For example, imagine a recent and archive table. When you query the last 7 days, and the last 30 are in recent - we can prune the archive."),
4939
+ new G("Example", "partial datasource recent_sales (\n order_id: orders.id,\n cu_id: customers.id,\n revenue: orders.revenue,\n date: orders.date\n)\ngrain (orders.id)\ncomplete where orders.date > current_date - 30\naddress warehouse.sales;\n\npartial datasource archive_sales (\n order_id: orders.id,\n cu_id: customers.id,\n revenue: orders.revenue,\n date: orders.date\n)\ngrain (orders.id)\ncomplete where orders.date <= current_date - 30\naddress warehouse.sales_archive;", "code"),
4940
+ new G("Partial Keys", "A query of the form `where orders.date > current_date -7 can safely resolve to just our recent. This lets us efficiently prune and select relevant datasources.")
4941
4941
  ]),
4942
- new W("Grains and Aggregation", [
4943
- new U("What Is a Grain?", "A grain represents the unique combination of keys. Tables and aggregations both have grains, which determine the minimum keys required to uniquely identify a row of data. For example, finding the total sales by customer id, anme, and address would be an aggregation to the grain of a customer id, no matter how many other customer properties are included. Properties of a key are implicitly dropped from any grain that includes that key, though a grain without the key associated with a property will include that property in the grain."),
4944
- new U("Example", "SELECT\n order_date,\n order_year,\n SUM(total_spent) AS total_revenue;", "code"),
4945
- new U("What Is a Grain?", "This query would aggregate revenue to the grain of order_date, as order year is a property of order date.")
4942
+ new K("Grains and Aggregation", [
4943
+ new G("What Is a Grain?", "A grain represents the unique combination of keys. Tables and aggregations both have grains, which determine the minimum keys required to uniquely identify a row of data. For example, finding the total sales by customer id, anme, and address would be an aggregation to the grain of a customer id, no matter how many other customer properties are included. Properties of a key are implicitly dropped from any grain that includes that key, though a grain without the key associated with a property will include that property in the grain."),
4944
+ new G("Example", "SELECT\n order_date,\n order_year,\n SUM(total_spent) AS total_revenue;", "code"),
4945
+ new G("What Is a Grain?", "This query would aggregate revenue to the grain of order_date, as order year is a property of order date.")
4946
4946
  ]),
4947
4947
  kn,
4948
4948
  On,
4949
4949
  An,
4950
- new W("Custom Functions", [
4951
- new U("Defining Functions", "Custom functions can be used to extend the language with reusable code macros. Functions are defined using the def keyword and have a list of arguments and are mapped to an expression. Any argument alias will be locally scoped within the function, but external concepts can be referenced as well."),
4952
- new U("Defining Functions", "This function will multiple the input concept by itself and then by whatever the value of the global scale_factor is. Note the @ - custom functions references require the @ prefix."),
4953
- new U("Example", "const scale_factor<-2;\ndef square_scale(x) -> x * x *scale_factor;\n\nSELECT\n number,\n @square_scale(number) AS squared\n;", "code"),
4954
- new U("Defining Functions", "Functions may have optional defaults by adding an `= value` after the argument name."),
4955
- new U("Example", "def pretty_percent(x, digits=2) -> round(x*100, digits)::string || '%';\n\nconst number<-.4555;\n\nSELECT\n number,\n @pretty_percent(number) AS percent,\n @pretty_percent(number, 3) AS three_percent\n;", "code")
4950
+ new K("Custom Functions", [
4951
+ new G("Defining Functions", "Custom functions can be used to extend the language with reusable code macros. Functions are defined using the def keyword and have a list of arguments and are mapped to an expression. Any argument alias will be locally scoped within the function, but external concepts can be referenced as well."),
4952
+ new G("Defining Functions", "This function will multiple the input concept by itself and then by whatever the value of the global scale_factor is. Note the @ - custom functions references require the @ prefix."),
4953
+ new G("Example", "const scale_factor<-2;\ndef square_scale(x) -> x * x *scale_factor;\n\nSELECT\n number,\n @square_scale(number) AS squared\n;", "code"),
4954
+ new G("Defining Functions", "Functions may have optional defaults by adding an `= value` after the argument name."),
4955
+ new G("Example", "def pretty_percent(x, digits=2) -> round(x*100, digits)::string || '%';\n\nconst number<-.4555;\n\nSELECT\n number,\n @pretty_percent(number) AS percent,\n @pretty_percent(number, 3) AS three_percent\n;", "code")
4956
4956
  ]),
4957
4957
  jn
4958
- ]), Nn = new G("Terms of Service", [new W("Terms of Service", [
4959
- new U("Last Updated", "February 22, 2025"),
4960
- new U("Service Description", "Trilogy Studio (referred to as \"IDE\" henceforth) enables users to interact with public data or data in databases they own. The IDE communicates with non-user services only to do basic telemetry and preprocessing of Trilogy code using the default language server (if a local one is not configured). The primary purpose of this preprocessing is to generate SQL to be returned to the browser for execution.", "section"),
4961
- new U("Data Privacy and Security", "No Data Collection: We do not collect, store, or process any of your data or database contents, beyond metadata provided in the model definition. All database interactions occur directly between your browser and your database through client-side JavaScript.", "section"),
4962
- new U("Local Processing", "All code execution and data processing occur locally in your browser. We have no access to your database credentials, queries, or results.", "subsection"),
4963
- new U("User Responsibility", "You are solely responsible for securing your database connections, managing database credentials, implementing appropriate security measures, backing up your data, and ensuring compliance with applicable data protection laws.", "subsection"),
4964
- new U("Service Limitations", "Browser Limitations: The IDE is subject to your browser's technical limitations, including memory constraints and processing capabilities.", "section"),
4965
- new U("Connection Requirements", "Stable internet connection required for IDE access. Database connectivity depends on your database configuration and network conditions.", "subsection"),
4966
- new U("User Obligations", "You agree to use the IDE in compliance with applicable laws, not attempt to circumvent browser security measures, not use the IDE for illegal or unauthorized purposes, maintain the security of your database credentials, and not redistribute or modify the IDE code without permission.", "section"),
4967
- new U("Disclaimers", "Service Availability: We provide the IDE \"as is\" and make no guarantees about its availability or functionality.", "section"),
4968
- new U("Security Disclaimer", "While we implement standard security measures in our code, we cannot guarantee the security of your database connections or local environment.", "subsection"),
4969
- new U("Liability Limitations", "We are not liable for data loss or corruption, database connection issues, browser performance problems, security breaches in your database, misuse of the IDE, or consequential damages.", "section"),
4970
- new U("User Rights", "You retain all rights to your code, your database, your data, and any outputs generated using the IDE.", "subsection"),
4971
- new U("Modifications", "We reserve the right to modify the IDE functionality, these Terms of Service, and supported features and libraries.", "section"),
4972
- new U("Termination", "You may stop using the IDE at any time.", "section"),
4973
- new U("Contact", "For questions about these terms, please create an issue on the github repository <a href='https://github.com/trilogy-data/trilogy-studio-core'>here</a>.", "section"),
4974
- new U("Agreement", "By using the IDE, you agree to these terms and conditions.", "conclusion")
4975
- ])]), Pn = [
4976
- new G("Studio", [
4977
- Tn,
4978
- Cn,
4958
+ ]), Nn = new q("Terms of Service", [new K("Terms of Service", [
4959
+ new G("Last Updated", "February 22, 2025"),
4960
+ new G("Service Description", "Trilogy Studio (referred to as \"IDE\" henceforth) enables users to interact with public data or data in databases they own. The IDE communicates with non-user services only to do basic telemetry and preprocessing of Trilogy code using the default language server (if a local one is not configured). The primary purpose of this preprocessing is to generate SQL to be returned to the browser for execution.", "section"),
4961
+ new G("Data Privacy and Security", "No Data Collection: We do not collect, store, or process any of your data or database contents, beyond metadata provided in the model definition. All database interactions occur directly between your browser and your database through client-side JavaScript.", "section"),
4962
+ new G("Local Processing", "All code execution and data processing occur locally in your browser. We have no access to your database credentials, queries, or results.", "subsection"),
4963
+ new G("User Responsibility", "You are solely responsible for securing your database connections, managing database credentials, implementing appropriate security measures, backing up your data, and ensuring compliance with applicable data protection laws.", "subsection"),
4964
+ new G("Service Limitations", "Browser Limitations: The IDE is subject to your browser's technical limitations, including memory constraints and processing capabilities.", "section"),
4965
+ new G("Connection Requirements", "Stable internet connection required for IDE access. Database connectivity depends on your database configuration and network conditions.", "subsection"),
4966
+ new G("User Obligations", "You agree to use the IDE in compliance with applicable laws, not attempt to circumvent browser security measures, not use the IDE for illegal or unauthorized purposes, maintain the security of your database credentials, and not redistribute or modify the IDE code without permission.", "section"),
4967
+ new G("Disclaimers", "Service Availability: We provide the IDE \"as is\" and make no guarantees about its availability or functionality.", "section"),
4968
+ new G("Security Disclaimer", "While we implement standard security measures in our code, we cannot guarantee the security of your database connections or local environment.", "subsection"),
4969
+ new G("Liability Limitations", "We are not liable for data loss or corruption, database connection issues, browser performance problems, security breaches in your database, misuse of the IDE, or consequential damages.", "section"),
4970
+ new G("User Rights", "You retain all rights to your code, your database, your data, and any outputs generated using the IDE.", "subsection"),
4971
+ new G("Modifications", "We reserve the right to modify the IDE functionality, these Terms of Service, and supported features and libraries.", "section"),
4972
+ new G("Termination", "You may stop using the IDE at any time.", "section"),
4973
+ new G("Contact", "For questions about these terms, please create an issue on the github repository <a href='https://github.com/trilogy-data/trilogy-studio-core'>here</a>.", "section"),
4974
+ new G("Agreement", "By using the IDE, you agree to these terms and conditions.", "conclusion")
4975
+ ])]), Pn = new K("Startup Scripts", [
4976
+ new G("Connection Setup", "Startup scripts prepare a connection before you use it, for example by creating tables, loading data, or applying session settings. Once enabled on a SQL editor, they run automatically whenever that connection connects or resets. No separate run-on-connect setting is needed. This does not make the connection itself connect automatically when Studio opens."),
4977
+ new G("Configure a Startup Script", "<ol>\n <li>Create or choose a connection in the Connections sidebar.</li>\n <li>Create a SQL editor associated with that connection and give it a descriptive name, such as <code>setup/load_sales.sql</code>.</li>\n <li>Write and test the setup SQL for your database.</li>\n <li>Enable <strong>Startup</strong> in the editor toolbar (the tooltip reads “Run this script on connection startup”).</li>\n <li>Save the editor and connection so the configuration is available after reloading Studio.</li>\n <li>Connect to run its startup scripts. If already connected, save your work, reload Studio, and reconnect to apply the setup.</li>\n </ol>"),
4978
+ new G("Example: Prepare a DuckDB Table", "For a DuckDB connection, the following SQL creates a small table that other editors can query. CREATE OR REPLACE makes it safe to rerun when reconnecting or resetting. Use the equivalent repeatable setup syntax for your database."),
4979
+ new G("DuckDB Setup SQL", "CREATE OR REPLACE TABLE startup_example AS\nSELECT 1 AS id, 'ready' AS status;", "code"),
4980
+ new G("When Scripts Run", "All editors marked Startup for that connection run after the database connection resets, including when it first connects. Changing the SQL or toggling Startup in the editor does not immediately rerun it. To rerun setup for an already connected session, save your work, reload Studio, and reconnect. The sidebar’s Refresh connection action only reloads database metadata when already connected; it does not reset the session or rerun startup scripts. Startup submits the editor contents directly as SQL, without Trilogy compilation. Only editors belonging to that connection are selected."),
4981
+ new G("Multiple Scripts and Dependencies", "Separate startup scripts are launched in parallel, with no guaranteed execution order. Put dependent SQL statements in one script, in the order they need to run, using a database that supports multi-statement scripts. Design setup to be repeatable: use CREATE OR REPLACE or IF NOT EXISTS where appropriate, and avoid inserts that duplicate data on each reconnect.", "tip"),
4982
+ new G("Diagnosing a Failure", "If a startup script fails, the connection is marked failed and the connection error popup identifies the script name (or remote file path), the connection, and the original database error. Open the named SQL editor, fix the SQL or the referenced data access, save it, and reconnect. For example, Startup script \"setup/load_sales.sql\" failed on connection \"analytics\" is followed by the database’s explanation. Other scripts may already have run; a startup failure does not roll back the entire group."),
4983
+ new G("Disable a Startup Script", "Open the SQL editor, turn off Startup, and save. Future connections and resets will skip that script. Disabling it does not undo SQL that already ran, such as creating a table or loading data.")
4984
+ ]), Fn = [
4985
+ new q("Studio", [
4979
4986
  wn,
4980
- En,
4981
- new W("Functionality", [
4982
- new U("Tip", "Trilogy Studio is a statically hosted website that uses a mix of precompiled scripts and dynamic imports for some connections to reduce bundle size."),
4983
- new U("Tip", "For Trilogy specific features, the studio relies on a pseudo-language server (there is a proper LSP available for vs-code, used in the vs-code extension, as well). By default this will use a cloud-hosted backend but studio can be configured to use a local address as well. "),
4984
- new U("Tip", "Trilogy Studio requires internet access to download certain dependencies - such as DuckDB WASM - on demand when utilizing a relevant connection. It can then be used offline if a local backend server is running. A pure offline option may be available in the future.", "tip")
4987
+ Sn,
4988
+ Cn,
4989
+ Tn,
4990
+ new K("Functionality", [
4991
+ new G("Tip", "Trilogy Studio is a statically hosted website that uses a mix of precompiled scripts and dynamic imports for some connections to reduce bundle size."),
4992
+ new G("Tip", "For Trilogy specific features, the studio relies on a pseudo-language server (there is a proper LSP available for vs-code, used in the vs-code extension, as well). By default this will use a cloud-hosted backend but studio can be configured to use a local address as well. "),
4993
+ new G("Tip", "Trilogy Studio requires internet access to download certain dependencies - such as DuckDB WASM - on demand when utilizing a relevant connection. It can then be used offline if a local backend server is running. A pure offline option may be available in the future.", "tip")
4985
4994
  ]),
4986
- new W("Navigation", [new U("Navigation", "On desktop, Trilogy Studio uses a left-hand navigation bar, a sidebar with context, and a main pane (which may be split) to present information. The sidebar is further split into sections; the first icons change the sidebar for the query editor, while the next section configures entirely new main screen experiences. Configuration/profile are accessible at the bottom. On mobile, Trilogy will instead use a top menu to access a navigation screen, and the main display will show only one particular field at a time."), new U("Tip", "Don't forget to save! By default, Trilogy Studio stores your editors, connections, and models in browser local storage. Use the save buttons on the left-hand nav to record changes. You can also use the keyboard shortcuts Ctrl + S to save your work.", "tip")]),
4987
- new W("Querying", [
4988
- new U("Querying", "A core goal of Trilogy Studio is to enable seamless running of Trilogy against backends. Raw SQL is fully supported as well to help with debugging, diagnostics, and other development tasks."),
4989
- new U("Editor Selection", "When creating an editor, you will select a type. Trilogy editors run Trilogy code; SQL editors run SQL. An editor must be associated with a connection and may be associated with a model."),
4990
- new U("Query Execution", "When running Trilogy queries, your command will first go to a backend server to be parsed/type-checked, then the output SQL will be returned to the editor to be run as a normal SQL query. SQL editors will submit directly to the configured connection without the first parsing pass.")
4995
+ new K("Navigation", [new G("Navigation", "On desktop, Trilogy Studio uses a left-hand navigation bar, a sidebar with context, and a main pane (which may be split) to present information. The sidebar is further split into sections; the first icons change the sidebar for the query editor, while the next section configures entirely new main screen experiences. Configuration/profile are accessible at the bottom. On mobile, Trilogy will instead use a top menu to access a navigation screen, and the main display will show only one particular field at a time."), new G("Tip", "Don't forget to save! By default, Trilogy Studio stores your editors, connections, and models in browser local storage. Use the save buttons on the left-hand nav to record changes. You can also use the keyboard shortcuts Ctrl + S to save your work.", "tip")]),
4996
+ new K("Querying", [
4997
+ new G("Querying", "A core goal of Trilogy Studio is to enable seamless running of Trilogy against backends. Raw SQL is fully supported as well to help with debugging, diagnostics, and other development tasks."),
4998
+ new G("Editor Selection", "When creating an editor, you will select a type. Trilogy editors run Trilogy code; SQL editors run SQL. An editor must be associated with a connection and may be associated with a model."),
4999
+ new G("Query Execution", "When running Trilogy queries, your command will first go to a backend server to be parsed/type-checked, then the output SQL will be returned to the editor to be run as a normal SQL query. SQL editors will submit directly to the configured connection without the first parsing pass.")
4991
5000
  ]),
4992
- new W("Connections", [
4993
- new U("Connections", "Editors must be associated with a connection. A connection represents a particular underlying backend database resource. Many IDEs can share one connection, but only a single query can be executed on a connection at a single point in time. Connections are also associated with a model, which enables configuration of additional editors as semantic sources."),
4994
- new U("Managing Connections", "You can view current connections below. Edit the model associated with a connection by clicking the model name next to it (or 'set model' if not set). Connections will not automatically connect on startup by default; click the connection button to connect. This connection view is always accessible through the connections page on the left side."),
4995
- new U("ConnectionList", "", "connections")
5001
+ new K("Connections", [
5002
+ new G("Connections", "Editors must be associated with a connection. A connection represents a particular underlying backend database resource. Many IDEs can share one connection, but only a single query can be executed on a connection at a single point in time. Connections are also associated with a model, which enables configuration of additional editors as semantic sources."),
5003
+ new G("Managing Connections", "You can view current connections below. Edit the model associated with a connection by clicking the model name next to it (or 'set model' if not set). Connections will not automatically connect on startup by default; click the connection button to connect. This connection view is always accessible through the connections page on the left side."),
5004
+ new G("ConnectionList", "", "connections"),
5005
+ new G("Startup Scripts", "To prepare a connection automatically on connect or reset, enable Startup on a SQL editor associated with it. See Studio → Startup Scripts for setup instructions, examples, and troubleshooting.")
4996
5006
  ]),
4997
- new W("Scheduling", [new U("Scheduling", "Scheduling dashboards/scripts is a future feature. Please upvote this github issue: <a href=\"https://github.com/trilogy-data/trilogy-studio-core/issues/75\" target=\"_blank\">Scheduling</a>.")])
5007
+ Pn,
5008
+ new K("Scheduling", [new G("Scheduling", "Scheduling dashboards/scripts is a future feature. Please upvote this github issue: <a href=\"https://github.com/trilogy-data/trilogy-studio-core/issues/75\" target=\"_blank\">Scheduling</a>.")])
4998
5009
  ]),
4999
- new G("Supported Databases", [
5000
- new W("Overview", [
5001
- new U("Supported Databases", "Trilogy Studio supports two classes of database connections: local browser engines and remote databases. Every editor runs against one of these connections, and Trilogy queries are compiled to the SQL dialect selected by that connection."),
5002
- new U("Local Browser Engines", "DuckDB and SQLite run locally inside the browser using WebAssembly. They require no database server or credentials, and uploaded data stays in the browser tab. These connections are convenient for local files, examples, prototyping, and offline query execution after their runtime assets have loaded."),
5003
- new U("Remote Databases", "BigQuery and Snowflake connect to externally hosted databases and require authentication. Queries run against the remote service, while Trilogy Studio manages the connection configuration, SQL dialect, schema browser, and query results in the interface. Use narrowly scoped credentials and review the Stored Info documentation before saving secrets.")
5010
+ new q("Supported Databases", [
5011
+ new K("Overview", [
5012
+ new G("Supported Databases", "Trilogy Studio supports two classes of database connections: local browser engines and remote databases. Every editor runs against one of these connections, and Trilogy queries are compiled to the SQL dialect selected by that connection."),
5013
+ new G("Local Browser Engines", "DuckDB and SQLite run locally inside the browser using WebAssembly. They require no database server or credentials, and uploaded data stays in the browser tab. These connections are convenient for local files, examples, prototyping, and offline query execution after their runtime assets have loaded."),
5014
+ new G("Remote Databases", "BigQuery and Snowflake connect to externally hosted databases and require authentication. Queries run against the remote service, while Trilogy Studio manages the connection configuration, SQL dialect, schema browser, and query results in the interface. Use narrowly scoped credentials and review the Stored Info documentation before saving secrets.")
5004
5015
  ]),
5005
- new W("DuckDB", [new U("DuckDB", "DuckDB is always available as an in-browser database using the DuckDB WASM integration. No authentication is required. DuckDB also natively supports CSV uploads through a widget available on the connection view. Most community DuckDB models reference publicly available CSV or Parquet files.")]),
5006
- new W("SQLite", [
5007
- new U("SQLite", "SQLite is available as an in-browser database using the sql.js WebAssembly integration. No server or authentication is required. Create a SQLite connection when you want to query an existing SQLite database or work with a lightweight local dataset using SQLite SQL."),
5008
- new U("Importing Data", "Open the SQLite connection and select Upload to import a .db or .sqlite database file. Importing a database replaces the current in-memory database for that connection. You can also upload a CSV file; Trilogy Studio creates a table named after the file, sanitizes its column names, and infers INTEGER, REAL, or TEXT column types from the data."),
5009
- new U("Browser Storage", "SQLite connection configuration is saved with the rest of your Studio workspace, but the contents of the in-browser database are memory-backed. After reloading the page, reconnect and upload the database or CSV files again before running queries that depend on them.", "tip")
5016
+ new K("DuckDB", [new G("DuckDB", "DuckDB is always available as an in-browser database using the DuckDB WASM integration. No authentication is required. DuckDB also natively supports CSV uploads through a widget available on the connection view. Most community DuckDB models reference publicly available CSV or Parquet files.")]),
5017
+ new K("SQLite", [
5018
+ new G("SQLite", "SQLite is available as an in-browser database using the sql.js WebAssembly integration. No server or authentication is required. Create a SQLite connection when you want to query an existing SQLite database or work with a lightweight local dataset using SQLite SQL."),
5019
+ new G("Importing Data", "Open the SQLite connection and select Upload to import a .db or .sqlite database file. Importing a database replaces the current in-memory database for that connection. You can also upload a CSV file; Trilogy Studio creates a table named after the file, sanitizes its column names, and infers INTEGER, REAL, or TEXT column types from the data."),
5020
+ new G("Browser Storage", "SQLite connection configuration is saved with the rest of your Studio workspace, but the contents of the in-browser database are memory-backed. After reloading the page, reconnect and upload the database or CSV files again before running queries that depend on them.", "tip")
5010
5021
  ]),
5011
- new W("BigQuery", [new U("BigQuery", "BigQuery is supported through OAuth authentication using your Google identity. If you wish to use a service key file, please upvote this GitHub issue: <a href=\"https://github.com/trilogy-data/trilogy-studio-core/issues/33\" target=\"_blank\">BQ Service Account Support</a>.")]),
5012
- new W("Snowflake", [new U("Snowflake", "Snowflake is supported with private key authentication. Read more at this link: <a href=\"https://docs.snowflake.com/en/user-guide/key-pair-auth\" target=\"_blank\">Snowflake Private Key Pairs</a>. Configure your Snowflake user with the public key, then provide the private key when connecting. Use caution when choosing whether to save this credential.")])
5022
+ new K("BigQuery", [new G("BigQuery", "BigQuery is supported through OAuth authentication using your Google identity. If you wish to use a service key file, please upvote this GitHub issue: <a href=\"https://github.com/trilogy-data/trilogy-studio-core/issues/33\" target=\"_blank\">BQ Service Account Support</a>.")]),
5023
+ new K("Snowflake", [new G("Snowflake", "Snowflake is supported with private key authentication. Read more at this link: <a href=\"https://docs.snowflake.com/en/user-guide/key-pair-auth\" target=\"_blank\">Snowflake Private Key Pairs</a>. Configure your Snowflake user with the public key, then provide the private key when connecting. Use caution when choosing whether to save this credential.")])
5013
5024
  ]),
5014
5025
  Mn,
5015
- new G("Privacy And Data", [
5016
- new W("Privacy Policy", [
5017
- new U("Last Updated", "February 22, 2025"),
5018
- new U("Introduction", "This Privacy Policy explains how Trilogy Studio handles your information. While most processing occurs in your browser, we utilize some external services to enhance your development experience.", "section"),
5019
- new U("Information We Collect", "Telemetry: We collect feature usage counts and patterns and coarse browser/geographic information to inform product decision and system performance using GoatTrack. This data is anonymous and does not include any personally identifiable information. Telemetry can be disabled in settings.", "section"),
5020
- new U("Query Processing", "We process Trilogy queries to transform them to SQL based on your model inputs if you use the default query service. Results are returned to your browser for execution. No actual database connections or credentials are transmitted.", "subsection"),
5021
- new U("What We Don't Collect", "We do not collect database credentials, database contents, query results, personal information, full source code, or authentication tokens, or any other personal information. Please report any issues with this to the github repository.", "section"),
5022
- new U("Data Processing", "Query Execution: All database execution happens between your browser and remote service. Query results never pass through our servers.", "section"),
5023
- new U("Security Measures", "Any saved local credentials will be encrypted in storage using a passphrase or Chrome credential storage. See Stored Info for more details.", "section"),
5024
- new U("Children's Privacy", "Trilogy Studio is intended for adult data professionals.", "section"),
5025
- new U("International Data", "Telemetry data may cross borders. SQL compilation happens in the US with default query service (fly.io).", "section"),
5026
- new U("Changes to Privacy Policy", "We may update this policy to reflect new features or services, changed functionality, legal requirements, and security improvements.", "section"),
5027
- new U("Google Account", "Trilogy Studio uses Google OAuth to authenticate users when using a Google Bigquery Oauth connection. Trilogy Studio uses a token provided by Google to authenticate your account. Trilogy Studio only requests scopes required for Bigquery read/write access, and the token never leaves your browser. It is used to communicate directly with Bigquery with the google javascript client library."),
5028
- new U("Contact Information", "For privacy-related questions: please post on the github repository <a href='https://github.com/trilogy-data/trilogy-studio-core'>here</a>.", "section"),
5029
- new U("Legal Rights", "You retain all legal rights regarding your personal data, your source code, your database contents, and generated outputs.", "section"),
5030
- new U("Effective Date", "This policy is effective as of February 22, 2025.", "conclusion")
5026
+ new q("Privacy And Data", [
5027
+ new K("Privacy Policy", [
5028
+ new G("Last Updated", "February 22, 2025"),
5029
+ new G("Introduction", "This Privacy Policy explains how Trilogy Studio handles your information. While most processing occurs in your browser, we utilize some external services to enhance your development experience.", "section"),
5030
+ new G("Information We Collect", "Telemetry: We collect feature usage counts and patterns and coarse browser/geographic information to inform product decision and system performance using GoatTrack. This data is anonymous and does not include any personally identifiable information. Telemetry can be disabled in settings.", "section"),
5031
+ new G("Query Processing", "We process Trilogy queries to transform them to SQL based on your model inputs if you use the default query service. Results are returned to your browser for execution. No actual database connections or credentials are transmitted.", "subsection"),
5032
+ new G("What We Don't Collect", "We do not collect database credentials, database contents, query results, personal information, full source code, or authentication tokens, or any other personal information. Please report any issues with this to the github repository.", "section"),
5033
+ new G("Data Processing", "Query Execution: All database execution happens between your browser and remote service. Query results never pass through our servers.", "section"),
5034
+ new G("Security Measures", "Any saved local credentials will be encrypted in storage using a passphrase or Chrome credential storage. See Stored Info for more details.", "section"),
5035
+ new G("Children's Privacy", "Trilogy Studio is intended for adult data professionals.", "section"),
5036
+ new G("International Data", "Telemetry data may cross borders. SQL compilation happens in the US with default query service (fly.io).", "section"),
5037
+ new G("Changes to Privacy Policy", "We may update this policy to reflect new features or services, changed functionality, legal requirements, and security improvements.", "section"),
5038
+ new G("Google Account", "Trilogy Studio uses Google OAuth to authenticate users when using a Google Bigquery Oauth connection. Trilogy Studio uses a token provided by Google to authenticate your account. Trilogy Studio only requests scopes required for Bigquery read/write access, and the token never leaves your browser. It is used to communicate directly with Bigquery with the google javascript client library."),
5039
+ new G("Contact Information", "For privacy-related questions: please post on the github repository <a href='https://github.com/trilogy-data/trilogy-studio-core'>here</a>.", "section"),
5040
+ new G("Legal Rights", "You retain all legal rights regarding your personal data, your source code, your database contents, and generated outputs.", "section"),
5041
+ new G("Effective Date", "This policy is effective as of February 22, 2025.", "conclusion")
5031
5042
  ]),
5032
- new W("Stored Info", [
5033
- new U("Local Data", "Trilogy Studio uses browser local storage for your editors and models. These do not leave your browser (except when a model is sent in to generate a query)."),
5034
- new U("Secret Storage", "For databases and LLM connections that require credentials (password, API Key) to access, Trilogy Studio can optionally also store them locally for reuse. [They will never be sent to a remote server]. It will attempt to use secure browser credential storage but will fall back to storing the secret in local browser storage encrypted with a pass phrase if the browser APIs are not available. Be careful storing credentials and be prepared to rotate - consider using a password manager."),
5035
- new U("Secret Storage", "Local storage is convenient, but if you have a browser secret manager, that is a great place to store secrets as well!", "tip"),
5036
- new U("Secret Storage", "A best practice is to use unique API tokens (such as for LLMs) and passwords in the studio, both to easily track usage and make rotation simple.", "tip")
5043
+ new K("Stored Info", [
5044
+ new G("Local Data", "Trilogy Studio uses browser local storage for your editors and models. These do not leave your browser (except when a model is sent in to generate a query)."),
5045
+ new G("Secret Storage", "For databases and LLM connections that require credentials (password, API Key) to access, Trilogy Studio can optionally also store them locally for reuse. [They will never be sent to a remote server]. It will attempt to use secure browser credential storage but will fall back to storing the secret in local browser storage encrypted with a pass phrase if the browser APIs are not available. Be careful storing credentials and be prepared to rotate - consider using a password manager."),
5046
+ new G("Secret Storage", "Local storage is convenient, but if you have a browser secret manager, that is a great place to store secrets as well!", "tip"),
5047
+ new G("Secret Storage", "A best practice is to use unique API tokens (such as for LLMs) and passwords in the studio, both to easily track usage and make rotation simple.", "tip")
5037
5048
  ]),
5038
- new W("Google", [new U("Google Account", "Trilogy Studio uses Google OAuth to authenticate users when using a Google Bigquery connection. Trilogy Studio uses a token provided by Google to authenticate your account through the interactive sign-in flow. Trilogy Studio only requests scopes required for Bigquery read/write access, and the token never leaves your browser. This token is only used to communicate directly with BigQuery with the standard google javascript client library.")]),
5039
- new W("Snowflake", [new U("Google Account", "Trilogy Studio supports private key authentication for Snowflake. You will need to provide the private key to connect after configuring your user with the public key portion in Snowflake directly. The rest of the authentication header can be derived from this.")]),
5040
- new W("Telemetry Details", [new U("Telemetry", "<a href=\"https://www.goatcounter.com/\">GoatCounter</a> is used to collect anonymized\n statistics about usage in the studio, such as feature usage. No uniquely identifying information is collected, though the system type, browser, screensize and country are recorded. You can disable telemetry in the settings menu.")])
5049
+ new K("Google", [new G("Google Account", "Trilogy Studio uses Google OAuth to authenticate users when using a Google Bigquery connection. Trilogy Studio uses a token provided by Google to authenticate your account through the interactive sign-in flow. Trilogy Studio only requests scopes required for Bigquery read/write access, and the token never leaves your browser. This token is only used to communicate directly with BigQuery with the standard google javascript client library.")]),
5050
+ new K("Snowflake", [new G("Google Account", "Trilogy Studio supports private key authentication for Snowflake. You will need to provide the private key to connect after configuring your user with the public key portion in Snowflake directly. The rest of the authentication header can be derived from this.")]),
5051
+ new K("Telemetry Details", [new G("Telemetry", "<a href=\"https://www.goatcounter.com/\">GoatCounter</a> is used to collect anonymized\n statistics about usage in the studio, such as feature usage. No uniquely identifying information is collected, though the system type, browser, screensize and country are recorded. You can disable telemetry in the settings menu.")])
5041
5052
  ]),
5042
5053
  Nn
5043
- ], Fn = /* @__PURE__ */ new Set([
5054
+ ], In = /* @__PURE__ */ new Set([
5044
5055
  "editors",
5045
5056
  "connections",
5046
5057
  "llm-connections",
@@ -5050,27 +5061,27 @@ var Dn = K.paragraphs[1], On = new W("Aggregate Functions", [
5050
5061
  "editor-validator",
5051
5062
  "connection-validator",
5052
5063
  "model-validator"
5053
- ]), In = /* @__PURE__ */ new Set(["Trilogy Reference"]), Ln = "Trilogy Language";
5054
- function Rn(e) {
5064
+ ]), Ln = /* @__PURE__ */ new Set(["Trilogy Reference"]), X = "Trilogy Language", Rn = "Example: ";
5065
+ function zn(e) {
5055
5066
  return e.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
5056
5067
  }
5057
- function zn(e) {
5058
- let t = Rn(e.content || ""), n = e.data?.function;
5068
+ function Bn(e) {
5069
+ let t = zn(e.content || ""), n = e.data?.function;
5059
5070
  return n && (t = [
5060
5071
  t,
5061
5072
  `Inputs: ${n.inputTypes.join(", ")}. Output: ${n.outputType} (${n.outputPurpose}).`,
5062
5073
  `Example: ${n.example}`
5063
5074
  ].filter(Boolean).join(" ")), t;
5064
5075
  }
5065
- var Y = null;
5066
- function Bn() {
5067
- if (Y) return Y;
5076
+ var Vn = null;
5077
+ function Hn() {
5078
+ if (Vn) return Vn;
5068
5079
  let e = [];
5069
- for (let t of Pn) {
5070
- let n = In.has(t.title);
5080
+ for (let t of Fn) {
5081
+ let n = Ln.has(t.title);
5071
5082
  for (let r of t.articles) for (let i of r.paragraphs) {
5072
- if (i.type && Fn.has(i.type)) continue;
5073
- let a = zn(i);
5083
+ if (i.type && In.has(i.type)) continue;
5084
+ let a = Bn(i);
5074
5085
  a && e.push({
5075
5086
  id: `${t.title}/${r.title}/${i.title}`,
5076
5087
  nodeTitle: t.title,
@@ -5081,27 +5092,37 @@ function Bn() {
5081
5092
  });
5082
5093
  }
5083
5094
  }
5084
- let t = x.split(/^(?=#{1,3} )/m);
5095
+ let t = S.split(/^(?=#{1,3} )/m);
5085
5096
  for (let n of t) {
5086
5097
  let t = n.match(/^#{1,3} (.+)/);
5087
5098
  if (!t) continue;
5088
5099
  let r = t[1].trim(), i = n.slice(t[0].length).trim();
5089
5100
  i && e.push({
5090
- id: `${Ln}/Syntax Reference/${r}`,
5091
- nodeTitle: Ln,
5101
+ id: `${X}/Syntax Reference/${r}`,
5102
+ nodeTitle: X,
5092
5103
  articleTitle: "Syntax Reference",
5093
5104
  paragraphTitle: r,
5094
5105
  content: i,
5095
5106
  kind: "language"
5096
5107
  });
5097
5108
  }
5098
- return Y = e, e;
5109
+ for (let t of re) t.body?.trim() && e.push({
5110
+ id: `${X}/${Rn}${t.name}/${t.title}`,
5111
+ nodeTitle: X,
5112
+ articleTitle: `${Rn}${t.name}`,
5113
+ paragraphTitle: t.title,
5114
+ content: `${t.summary}
5115
+
5116
+ ${t.body.trim()}`,
5117
+ kind: "language"
5118
+ });
5119
+ return Vn = e, e;
5099
5120
  }
5100
- function X(e) {
5121
+ function Z(e) {
5101
5122
  return e.toLowerCase().split(/[^a-z0-9_]+/).filter((e) => e.length > 1);
5102
5123
  }
5103
- function Vn(e, t) {
5104
- let n = X(e.paragraphTitle), r = X(e.articleTitle), i = e.content.toLowerCase(), a = X(e.content), o = 0;
5124
+ function Un(e, t) {
5125
+ let n = Z(e.paragraphTitle), r = Z(e.articleTitle), i = e.content.toLowerCase(), a = Z(e.content), o = 0;
5105
5126
  for (let e of t) {
5106
5127
  n.includes(e) ? o += 3 : e.length >= 4 && n.some((t) => t.startsWith(e)) && (o += 1.5), r.includes(e) && (o += 2);
5107
5128
  let t = a.filter((t) => t === e).length;
@@ -5109,29 +5130,29 @@ function Vn(e, t) {
5109
5130
  }
5110
5131
  return o / Math.log(Math.max(e.content.length, 50));
5111
5132
  }
5112
- function Hn(e, t) {
5133
+ function Wn(e, t) {
5113
5134
  let n = e.content.toLowerCase(), r = -1;
5114
5135
  for (let e of t) if (r = n.indexOf(e), r >= 0) break;
5115
5136
  r < 0 && (r = 0);
5116
5137
  let i = Math.max(0, r - 80), a = Math.min(e.content.length, r + 160);
5117
5138
  return `${i > 0 ? "…" : ""}${e.content.slice(i, a)}${a < e.content.length ? "…" : ""}`;
5118
5139
  }
5119
- function Un(e, t = {}) {
5120
- let n = X(e);
5140
+ function Gn(e, t = {}) {
5141
+ let n = Z(e);
5121
5142
  if (n.length === 0) return [];
5122
5143
  let r = t.limit ?? 5;
5123
- return Bn().filter((e) => !t.kind || e.kind === t.kind).map((e) => ({
5144
+ return Hn().filter((e) => !t.kind || e.kind === t.kind).map((e) => ({
5124
5145
  entry: e,
5125
- score: Vn(e, n)
5146
+ score: Un(e, n)
5126
5147
  })).filter((e) => e.score > 0).sort((e, t) => t.score - e.score).slice(0, r).map((e) => ({
5127
5148
  ...e,
5128
- snippet: Hn(e.entry, n)
5149
+ snippet: Wn(e.entry, n)
5129
5150
  }));
5130
5151
  }
5131
- function Wn(e) {
5152
+ function Kn(e) {
5132
5153
  let [t, n] = e.split("/");
5133
5154
  if (!t || !n) return null;
5134
- let r = Bn().filter((e) => e.nodeTitle === t && e.articleTitle === n);
5155
+ let r = Hn().filter((e) => e.nodeTitle === t && e.articleTitle === n);
5135
5156
  return r.length === 0 ? null : {
5136
5157
  id: `${t}/${n}`,
5137
5158
  nodeTitle: t,
@@ -5144,7 +5165,7 @@ function Wn(e) {
5144
5165
  }
5145
5166
  //#endregion
5146
5167
  //#region llm/registry/packs/docsPack.ts
5147
- function Gn() {
5168
+ function qn() {
5148
5169
  return [
5149
5170
  {
5150
5171
  pack: "docs",
@@ -5176,7 +5197,7 @@ function Gn() {
5176
5197
  }
5177
5198
  },
5178
5199
  execute: async (e) => {
5179
- let t = Un(String(e.query ?? ""), {
5200
+ let t = Gn(String(e.query ?? ""), {
5180
5201
  limit: Number(e.limit) || 5,
5181
5202
  kind: e.kind
5182
5203
  });
@@ -5204,7 +5225,7 @@ function Gn() {
5204
5225
  }
5205
5226
  },
5206
5227
  execute: async (e) => {
5207
- let t = Wn(String(e.doc_id ?? ""));
5228
+ let t = Kn(String(e.doc_id ?? ""));
5208
5229
  return t ? {
5209
5230
  success: !0,
5210
5231
  message: `${t.nodeTitle} > ${t.articleTitle}\n\n${t.paragraphs.map((e) => `## ${e.title}\n${e.content}`).join("\n\n")}`
@@ -5233,7 +5254,7 @@ function Gn() {
5233
5254
  hint: "App navigation is not available in this embedding context."
5234
5255
  },
5235
5256
  execute: async (e, t) => {
5236
- let n = Wn(String(e.doc_id ?? ""));
5257
+ let n = Kn(String(e.doc_id ?? ""));
5237
5258
  if (!n) return {
5238
5259
  success: !1,
5239
5260
  error: `Documentation "${e.doc_id}" not found. Use search_docs to find valid ids.`
@@ -5253,7 +5274,7 @@ function Gn() {
5253
5274
  }
5254
5275
  //#endregion
5255
5276
  //#region llm/registry/packs/contextPack.ts
5256
- function Kn() {
5277
+ function Jn() {
5257
5278
  return [{
5258
5279
  pack: "context",
5259
5280
  definition: {
@@ -5276,20 +5297,20 @@ function Kn() {
5276
5297
  }
5277
5298
  //#endregion
5278
5299
  //#region llm/registry/index.ts
5279
- function qn() {
5280
- let e = new Ut();
5281
- return e.registerAll(Xt()), e.registerAll(Zt()), e.registerAll(nn()), e.registerAll(un()), e.registerAll(gn()), e.registerAll(Sn()), e.registerAll(Gn()), e.registerAll(Kn()), e.registerAll(Qt()), e;
5300
+ function Yn() {
5301
+ let e = new Vt();
5302
+ return e.registerAll(Jt()), e.registerAll(Yt()), e.registerAll(en()), e.registerAll(ln()), e.registerAll(hn()), e.registerAll(xn()), e.registerAll(qn()), e.registerAll(Jn()), e.registerAll(Xt()), e;
5282
5303
  }
5283
- var Jn = null;
5284
- function Z() {
5285
- return Jn ||= qn(), Jn;
5304
+ var Xn = null;
5305
+ function Zn() {
5306
+ return Xn ||= Yn(), Xn;
5286
5307
  }
5287
5308
  //#endregion
5288
5309
  //#region llm/architectAgentPrompt.ts
5289
- function Yn(e) {
5310
+ function Qn(e) {
5290
5311
  return ` - ${e.name} (${e.type}, ${e.size} bytes)`;
5291
5312
  }
5292
- function Xn() {
5313
+ function $n() {
5293
5314
  return `You are an ARCHITECT subchat. Your job is to build a Trilogy data model from the raw files attached to this project. The user does not see this conversation — your overseer does. Work autonomously; when finished, call return_to_user with a brief summary.
5294
5315
 
5295
5316
  TYPICAL WORKFLOW:
@@ -5304,7 +5325,7 @@ TYPICAL WORKFLOW:
5304
5325
  6. return_to_user with a list of the files created and any caveats.
5305
5326
 
5306
5327
  TRILOGY LANGUAGE REFERENCE:
5307
- ${x}
5328
+ ${S}
5308
5329
 
5309
5330
  OPERATIONAL GUIDELINES:
5310
5331
  - One .preql file per logical entity (one per CSV is a good default).
@@ -5313,15 +5334,15 @@ OPERATIONAL GUIDELINES:
5313
5334
  - Don't return until at least one file validates cleanly and a smoke query succeeds, OR you've exhausted reasonable attempts and need to report blockers.
5314
5335
  - Errors from the resolver are authoritative — read them carefully; don't guess fixes.`;
5315
5336
  }
5316
- function Zn(e) {
5317
- let t = e.files.length > 0 ? `FILES IN THIS PROJECT:\n${e.files.map(Yn).join("\n")}` : "No files in this project yet.", n = e.dataConnectionName ? `DATA CONNECTION: ${e.dataConnectionName}${e.isDataConnectionActive ? "" : " (not yet connected — first run_trilogy_query will boot it)"}` : "No data connection bound.";
5318
- return `${e.instructionsOverride?.trim() || Xn()}
5337
+ function er(e) {
5338
+ let t = e.files.length > 0 ? `FILES IN THIS PROJECT:\n${e.files.map(Qn).join("\n")}` : "No files in this project yet.", n = e.dataConnectionName ? `DATA CONNECTION: ${e.dataConnectionName}${e.isDataConnectionActive ? "" : " (not yet connected — first run_trilogy_query will boot it)"}` : "No data connection bound.";
5339
+ return `${e.instructionsOverride?.trim() || $n()}
5319
5340
 
5320
5341
  ${t}
5321
5342
  ${n}
5322
5343
  `;
5323
5344
  }
5324
- var Qn = [
5345
+ var tr = [
5325
5346
  {
5326
5347
  name: "list_project_files",
5327
5348
  description: "List every file attached to the current project: name, type (preql / csv / sql / markdown / etc.), size in bytes.",
@@ -5461,12 +5482,12 @@ var Qn = [
5461
5482
  required: ["query"]
5462
5483
  }
5463
5484
  },
5464
- v
5465
- ], $n = [];
5466
- function er(e) {
5467
- $n.push(e);
5485
+ y
5486
+ ], nr = [];
5487
+ function rr(e) {
5488
+ nr.push(e);
5468
5489
  }
5469
- var tr = Oe("chats", {
5490
+ var ir = Ee("chats", {
5470
5491
  state: () => ({
5471
5492
  chats: {},
5472
5493
  activeChatId: "",
@@ -5486,7 +5507,7 @@ var tr = Oe("chats", {
5486
5507
  },
5487
5508
  actions: {
5488
5509
  newChat(e, t = "", n, r = "", i = {}) {
5489
- let a = new c({
5510
+ let a = new o({
5490
5511
  llmConnectionName: e,
5491
5512
  dataConnectionName: t,
5492
5513
  dataConnectionId: r,
@@ -5498,7 +5519,7 @@ var tr = Oe("chats", {
5498
5519
  this.chats[e.id] = e;
5499
5520
  },
5500
5521
  removeChat(e) {
5501
- this.chats[e] && (this.chats[e].deleted = !0, this.chats[e].changed = !0, this.activeChatId === e && (this.activeChatId = ""), $n.forEach((t) => {
5522
+ this.chats[e] && (this.chats[e].deleted = !0, this.chats[e].changed = !0, this.activeChatId === e && (this.activeChatId = ""), nr.forEach((t) => {
5502
5523
  try {
5503
5524
  t(e);
5504
5525
  } catch (e) {
@@ -5544,7 +5565,7 @@ var tr = Oe("chats", {
5544
5565
  },
5545
5566
  loadChats(e) {
5546
5567
  Object.entries(e).forEach(([e, t]) => {
5547
- this.chats[e] = c.fromSerialized(t);
5568
+ this.chats[e] = o.fromSerialized(t);
5548
5569
  });
5549
5570
  },
5550
5571
  serializeChats() {
@@ -5619,7 +5640,7 @@ var tr = Oe("chats", {
5619
5640
  error: !0
5620
5641
  }), { response: "No LLM connection available. Please configure an LLM provider first." };
5621
5642
  let d = a.connections[l];
5622
- d && await u(d, i), this.startExecution(e);
5643
+ d && await ve(d, i), this.startExecution(e);
5623
5644
  let f = this.chatExecutions[e], p = () => this.chatExecutions[e] === f, m = f?.abortController?.signal, h = !1;
5624
5645
  this.addMessageToChat(e, {
5625
5646
  role: "user",
@@ -5627,7 +5648,7 @@ var tr = Oe("chats", {
5627
5648
  hidden: r.hiddenUserMessage ?? !1
5628
5649
  });
5629
5650
  try {
5630
- let u = { generateCompletion: async (t, n, r) => {
5651
+ let d = { generateCompletion: async (t, n, r) => {
5631
5652
  let o = {
5632
5653
  ...n,
5633
5654
  signal: m,
@@ -5636,7 +5657,7 @@ var tr = Oe("chats", {
5636
5657
  }
5637
5658
  }, s = await a.generateCompletion(t, o, r);
5638
5659
  return this.clearRateLimitBackoff(e), s.usage && (i.lastContextTokens = s.usage.promptTokens + (s.usage.cacheCreationTokens ?? 0) + (s.usage.cacheReadTokens ?? 0), i.changed = !0), s;
5639
- } }, d = {
5660
+ } }, f = {
5640
5661
  addMessage: (t) => {
5641
5662
  this.addMessageToChat(e, t);
5642
5663
  },
@@ -5649,16 +5670,16 @@ var tr = Oe("chats", {
5649
5670
  });
5650
5671
  },
5651
5672
  getMessages: () => i.getLLMMessages()
5652
- }, f = r.overrides ? { getToolExecutor: () => ({ executeToolCall: r.overrides.executeToolCall }) } : i.kind === "overseer" ? (() => {
5673
+ }, p = r.overrides ? { getToolExecutor: () => ({ executeToolCall: r.overrides.executeToolCall }) } : i.kind === "overseer" ? (() => {
5653
5674
  if (!n.projectStore) throw Error("Overseer chats require deps.projectStore");
5654
- let t = new Nt(this, n.projectStore, e, n, n.dashboardStore);
5675
+ let t = new jt(this, n.projectStore, e, n, n.dashboardStore);
5655
5676
  return { getToolExecutor: () => t };
5656
5677
  })() : i.kind === "architect" ? (() => {
5657
5678
  if (!n.projectStore) throw Error("Architect subchats require deps.projectStore");
5658
- let t = new It(this, n.projectStore, c, o, s, e);
5679
+ let t = new Pt(this, n.projectStore, c, o, s, e);
5659
5680
  return { getToolExecutor: () => t };
5660
5681
  })() : (() => {
5661
- let t = Z().createExecutor("chat", {
5682
+ let t = Zn().createExecutor("chat", {
5662
5683
  connectionStore: o,
5663
5684
  editorStore: c,
5664
5685
  chatStore: this,
@@ -5682,20 +5703,20 @@ var tr = Oe("chats", {
5682
5703
  }
5683
5704
  } };
5684
5705
  return { getToolExecutor: () => a };
5685
- })(), p = {
5706
+ })(), g = {
5686
5707
  setActiveToolName: (t) => this.setActiveToolName(e, t),
5687
5708
  checkAborted: () => m?.aborted ?? !1,
5688
5709
  isPaused: () => this.chatExecutions[e]?.paused ?? !1
5689
- }, g = r.overrides ? r.overrides.buildSystemPrompt : () => this.buildSystemPrompt(e, n, r.disabledTools), ee = r.overrides ? void 0 : async (e, t) => {
5710
+ }, ee = r.overrides ? r.overrides.buildSystemPrompt : () => this.buildSystemPrompt(e, n, r.disabledTools), te = r.overrides ? void 0 : async (e, t) => {
5690
5711
  t.triggersSymbolRefresh && r.onSymbolsRefresh && await r.onSymbolsRefresh();
5691
5712
  };
5692
- h = !!(await ie(t, l, u, d, f, p, {
5693
- tools: r.overrides ? r.overrides.tools : i.kind === "overseer" ? Ge : i.kind === "architect" ? Qn : ge(se(Z().getToolsetForContext("chat"), r.disabledTools), r.extraTools),
5713
+ h = !!(await u(t, l, d, f, p, g, {
5714
+ tools: r.overrides ? r.overrides.tools : i.kind === "overseer" ? We : i.kind === "architect" ? tr : Ce(fe(Zn().getToolsetForContext("chat"), r.disabledTools), r.extraTools),
5694
5715
  maxIterations: r.overrides?.maxIterations ?? 50,
5695
5716
  noToolCallReminder: r.overrides?.noToolCallReminder ?? "You must call a tool to proceed. If you are finished, call return_to_user with a summary of what was done. Do not respond with text only.",
5696
5717
  terminateOnNoToolCall: r.overrides?.terminateOnNoToolCall,
5697
- buildSystemPrompt: g,
5698
- onToolResult: ee
5718
+ buildSystemPrompt: ee,
5719
+ onToolResult: te
5699
5720
  })).stopped;
5700
5721
  } catch (t) {
5701
5722
  if (t instanceof DOMException && t.name === "AbortError" || m?.aborted) {
@@ -5720,7 +5741,7 @@ var tr = Oe("chats", {
5720
5741
  if (!r) return;
5721
5742
  let i = t.llmConnectionStore.connections[n.llmConnectionName] ?? t.llmConnectionStore.connections[r.llmConnectionName] ?? null, a;
5722
5743
  if (i) try {
5723
- a = await Se(i, n, "completion");
5744
+ a = await le(i, n, "completion");
5724
5745
  } catch (e) {
5725
5746
  console.error("subchat summarization failed; falling back", e), a = n.messages[n.messages.length - 1]?.content?.trim() || "(no summary available — subchat finished but the auto-summary call failed)";
5726
5747
  }
@@ -5743,7 +5764,7 @@ var tr = Oe("chats", {
5743
5764
  id: e.id,
5744
5765
  name: e.name.replace(/\//g, "."),
5745
5766
  alias: ""
5746
- })) : [], d = ye({
5767
+ })) : [], d = xe({
5747
5768
  dataConnectionName: o,
5748
5769
  availableConnections: s,
5749
5770
  availableConcepts: [],
@@ -5754,7 +5775,7 @@ var tr = Oe("chats", {
5754
5775
  });
5755
5776
  if (r.kind === "analyst") {
5756
5777
  let e = r.parentProjectId || t.projectStore?.activeProjectId || "";
5757
- return `${(e ? t.projectStore?.projects[e] : null)?.promptOverrides.analyst?.trim() || Be}\n\n${d}`;
5778
+ return `${(e ? t.projectStore?.projects[e] : null)?.promptOverrides.analyst?.trim() || ze}\n\n${d}`;
5758
5779
  }
5759
5780
  return d;
5760
5781
  },
@@ -5766,7 +5787,7 @@ var tr = Oe("chats", {
5766
5787
  type: e.type,
5767
5788
  size: (e.contents || "").length
5768
5789
  })) : [], o = i?.dataConnectionId && t.connectionStore.connections[i.dataConnectionId] || null;
5769
- return Zn({
5790
+ return er({
5770
5791
  projectName: i?.name,
5771
5792
  projectDescription: i?.description,
5772
5793
  files: a,
@@ -5790,7 +5811,7 @@ var tr = Oe("chats", {
5790
5811
  });
5791
5812
  }
5792
5813
  let s = a ? a.editorIds.map((e) => i?.editors[e]?.name).filter((e) => !!e) : [];
5793
- return We({
5814
+ return Ue({
5794
5815
  projectName: a?.name,
5795
5816
  projectDescription: a?.description,
5796
5817
  subchats: o,
@@ -5803,14 +5824,14 @@ var tr = Oe("chats", {
5803
5824
  });
5804
5825
  //#endregion
5805
5826
  //#region llm/editorRefinementRuntime.ts
5806
- function nr(e, t) {
5807
- let { editorStore: n, chatStore: r, llmConnectionStore: i } = t, a = n.editors[e], o = a?.refinementSession;
5808
- if (!a || !o) return null;
5809
- if (o.refinementChatId) {
5810
- let e = r.chats[o.refinementChatId];
5811
- if (e && !e.deleted) return o.refinementChatId;
5827
+ function ar(e, t) {
5828
+ let { editorStore: n, chatStore: r, llmConnectionStore: i } = t, a = n.editors[e], s = a?.refinementSession;
5829
+ if (!a || !s) return null;
5830
+ if (s.refinementChatId) {
5831
+ let e = r.chats[s.refinementChatId];
5832
+ if (e && !e.deleted) return s.refinementChatId;
5812
5833
  }
5813
- let s = new c({
5834
+ let c = new o({
5814
5835
  name: `Refine: ${a.name}`,
5815
5836
  llmConnectionName: i.activeConnection || "",
5816
5837
  dataConnectionName: a.connection,
@@ -5818,29 +5839,29 @@ function nr(e, t) {
5818
5839
  sourceRefId: e,
5819
5840
  storage: "ephemeral"
5820
5841
  });
5821
- return r.addChat(s), n.updateRefinementSession(e, { refinementChatId: s.id }), s.id;
5842
+ return r.addChat(c), n.updateRefinementSession(e, { refinementChatId: c.id }), c.id;
5822
5843
  }
5823
5844
  var Q = /* @__PURE__ */ new Map(), $ = /* @__PURE__ */ new Map();
5824
- function rr(e) {
5845
+ function or(e) {
5825
5846
  let t = $.get(e);
5826
- return t || (t = _e(), $.set(e, t)), t;
5847
+ return t || (t = me(), $.set(e, t)), t;
5827
5848
  }
5828
- er((e) => {
5849
+ rr((e) => {
5829
5850
  Q.delete(e), $.delete(e);
5830
5851
  });
5831
- function ir(e) {
5852
+ function sr(e) {
5832
5853
  Q.delete(e);
5833
5854
  }
5834
- async function ar(e) {
5835
- let { editorId: t, message: n, stores: r, deps: a, callbacks: o = {} } = e, { editorStore: c, chatStore: l } = r, u = c.editors[t];
5836
- if (!u || !u.refinementSession) return;
5837
- let d = nr(t, r);
5838
- if (!d) return;
5839
- let f = !1, p, m = () => {
5840
- let e = c.editors[t]?.refinementSession;
5855
+ async function cr(e) {
5856
+ let { editorId: t, message: n, stores: r, deps: i, callbacks: a = {} } = e, { editorStore: o, chatStore: s } = r, c = o.editors[t];
5857
+ if (!c || !c.refinementSession) return;
5858
+ let l = ar(t, r);
5859
+ if (!l) return;
5860
+ let u = !1, d, f = () => {
5861
+ let e = o.editors[t]?.refinementSession;
5841
5862
  return e ? {
5842
- editorType: u.type,
5843
- connectionName: u.connection,
5863
+ editorType: c.type,
5864
+ connectionName: c.connection,
5844
5865
  editorContents: e.currentContent,
5845
5866
  selectedText: e.selectedText,
5846
5867
  selectionRange: e.selectionRange,
@@ -5849,7 +5870,7 @@ async function ar(e) {
5849
5870
  let i = n;
5850
5871
  if (r && e.selectionRange) {
5851
5872
  let r = e.currentContent.slice(0, e.selectionRange.start), a = e.currentContent.slice(e.selectionRange.end);
5852
- i = r + n + a, c.updateRefinementSession(t, {
5873
+ i = r + n + a, o.updateRefinementSession(t, {
5853
5874
  currentContent: i,
5854
5875
  selectedText: n,
5855
5876
  selectionRange: {
@@ -5857,20 +5878,20 @@ async function ar(e) {
5857
5878
  end: e.selectionRange.start + n.length
5858
5879
  }
5859
5880
  });
5860
- } else c.updateRefinementSession(t, { currentContent: i });
5861
- o.onContentChange?.(i, r);
5881
+ } else o.updateRefinementSession(t, { currentContent: i });
5882
+ a.onContentChange?.(i, r);
5862
5883
  },
5863
5884
  onChartConfigChange: (e) => {
5864
- c.updateRefinementSession(t, { currentChartConfig: e }), o.onChartConfigChange?.(e);
5885
+ o.updateRefinementSession(t, { currentChartConfig: e }), a.onChartConfigChange?.(e);
5865
5886
  },
5866
5887
  onFinish: (e) => {
5867
- f = !0, p = e;
5888
+ u = !0, d = e;
5868
5889
  },
5869
- onRunActiveEditorQuery: o.onRunActiveEditorQuery ? () => o.onRunActiveEditorQuery() : void 0,
5870
- getCurrentResults: () => c.editors[t]?.results ?? null
5890
+ onRunActiveEditorQuery: a.onRunActiveEditorQuery ? () => a.onRunActiveEditorQuery() : void 0,
5891
+ getCurrentResults: () => o.editors[t]?.results ?? null
5871
5892
  } : {
5872
- editorType: u.type,
5873
- connectionName: u.connection,
5893
+ editorType: c.type,
5894
+ connectionName: c.connection,
5874
5895
  editorContents: "",
5875
5896
  selectedText: void 0,
5876
5897
  selectionRange: void 0,
@@ -5880,32 +5901,32 @@ async function ar(e) {
5880
5901
  onFinish: () => {}
5881
5902
  };
5882
5903
  };
5883
- await l.executeMessage(d, n, a, { overrides: {
5884
- tools: s(u.type),
5885
- executeToolCall: (e, t) => new we(a.queryExecutionService, a.connectionStore, m(), c, rr(d)).executeToolCall(e, t),
5904
+ await s.executeMessage(l, n, i, { overrides: {
5905
+ tools: p(c.type),
5906
+ executeToolCall: (e, t) => new Se(i.queryExecutionService, i.connectionStore, f(), o, or(l)).executeToolCall(e, t),
5886
5907
  buildSystemPrompt: () => {
5887
- let e = Q.get(d);
5908
+ let e = Q.get(l);
5888
5909
  if (!e) {
5889
- let n = c.editors[t]?.refinementSession;
5890
- e = i({
5891
- editorType: u.type,
5892
- connectionName: u.connection,
5893
- editorContents: n?.currentContent ?? u.contents,
5910
+ let n = o.editors[t]?.refinementSession;
5911
+ e = m({
5912
+ editorType: c.type,
5913
+ connectionName: c.connection,
5914
+ editorContents: n?.currentContent ?? c.contents,
5894
5915
  selectedText: n?.selectedText,
5895
5916
  selectionRange: n?.selectionRange,
5896
5917
  chartConfig: n?.currentChartConfig,
5897
- completionSymbols: u.completionSymbols || []
5898
- }), Q.set(d, e);
5918
+ completionSymbols: c.completionSymbols || []
5919
+ }), Q.set(l, e);
5899
5920
  }
5900
5921
  return e;
5901
5922
  },
5902
5923
  maxIterations: 20,
5903
5924
  noToolCallReminder: "You must call a tool to proceed. If you are finished with the requested changes, call request_close to wrap up the session. Do not respond with text only."
5904
- } }), f && c.editors[t]?.refinementSession && c.acceptRefinement(t, { onFinish: () => o.onFinish?.(p) });
5925
+ } }), u && o.editors[t]?.refinementSession && o.acceptRefinement(t, { onFinish: () => a.onFinish?.(d) });
5905
5926
  }
5906
- function or(e, t) {
5927
+ function lr(e, t) {
5907
5928
  let n = t.editorStore.editors[e]?.refinementSession?.refinementChatId;
5908
- n && (t.chatStore.isChatExecuting(n) && t.chatStore.stopExecution(n), t.chatStore.removeChat(n), ir(n), $.delete(n));
5929
+ n && (t.chatStore.isChatExecuting(n) && t.chatStore.stopExecution(n), t.chatStore.removeChat(n), sr(n), $.delete(n));
5909
5930
  }
5910
5931
  //#endregion
5911
- export { Me as A, w as C, C as D, T as E, Ne as M, je as N, Ie as O, E as S, Ae as T, it as _, Z as a, ze as b, xt as c, A as d, st as f, rt as g, ot as h, tr as i, Fe as j, Pe as k, bt as l, at as m, ar as n, Pn as o, ct as p, er as r, Mt as s, or as t, gt as u, nt as v, S as w, Re as x, k as y };
5932
+ export { Ae as A, D as C, E as D, O as E, je as M, ke as N, Pe as O, k as S, Oe as T, nt as _, Zn as a, Le as b, yt as c, N as d, at as f, tt as g, it as h, ir as i, Ne as j, Me as k, vt as l, rt as m, cr as n, Fn as o, ot as p, rr as r, At as s, lr as t, mt as u, et as v, T as w, Ie as x, M as y };