@kaelio/ktx 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/assets/python/kaelio_ktx-0.16.0-py3-none-any.whl +0 -0
  2. package/assets/python/manifest.json +4 -4
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/commands/setup-commands.js +2 -0
  5. package/dist/connection-drivers.d.ts +1 -1
  6. package/dist/connection-drivers.js +2 -0
  7. package/dist/connection.js +1 -0
  8. package/dist/connectors/athena/connector.d.ts +160 -0
  9. package/dist/connectors/athena/connector.js +378 -0
  10. package/dist/connectors/athena/dialect.d.ts +31 -0
  11. package/dist/connectors/athena/dialect.js +146 -0
  12. package/dist/connectors/athena/live-database-introspection.d.ts +10 -0
  13. package/dist/connectors/athena/live-database-introspection.js +24 -0
  14. package/dist/connectors/duckdb/connector.d.ts +60 -0
  15. package/dist/connectors/duckdb/connector.js +282 -0
  16. package/dist/connectors/duckdb/dialect.d.ts +31 -0
  17. package/dist/connectors/duckdb/dialect.js +155 -0
  18. package/dist/connectors/duckdb/federated-attach.js +7 -0
  19. package/dist/connectors/duckdb/federated-executor.js +7 -13
  20. package/dist/connectors/duckdb/live-database-introspection.d.ts +8 -0
  21. package/dist/connectors/duckdb/live-database-introspection.js +24 -0
  22. package/dist/connectors/shared/duckdb-json-safe.d.ts +3 -0
  23. package/dist/connectors/shared/duckdb-json-safe.js +12 -0
  24. package/dist/context/connections/connection-type-dialect.d.ts +2 -0
  25. package/dist/context/connections/connection-type-dialect.js +20 -0
  26. package/dist/context/connections/connection-type.d.ts +6 -15
  27. package/dist/context/connections/connection-type.js +1 -10
  28. package/dist/context/connections/dialects.js +4 -0
  29. package/dist/context/connections/drivers.js +38 -0
  30. package/dist/context/connections/federation.d.ts +1 -1
  31. package/dist/context/connections/federation.js +6 -5
  32. package/dist/context/connections/local-warehouse-descriptor.d.ts +1 -0
  33. package/dist/context/connections/local-warehouse-descriptor.js +10 -0
  34. package/dist/context/connections/project-sql-executor.d.ts +24 -0
  35. package/dist/context/connections/project-sql-executor.js +56 -0
  36. package/dist/context/connections/query-policy.d.ts +12 -0
  37. package/dist/context/connections/query-policy.js +46 -0
  38. package/dist/context/core/config-reference.js +11 -1
  39. package/dist/context/daemon/semantic-layer-compute.d.ts +16 -0
  40. package/dist/context/daemon/semantic-layer-compute.js +44 -2
  41. package/dist/context/ingest/adapters/looker/mapping.d.ts +1 -0
  42. package/dist/context/ingest/adapters/looker/mapping.js +3 -10
  43. package/dist/context/ingest/adapters/looker/types.d.ts +6 -15
  44. package/dist/context/ingest/adapters/metabase/mapping.d.ts +1 -0
  45. package/dist/context/ingest/adapters/metabase/mapping.js +1 -0
  46. package/dist/context/ingest/adapters/sigma/chunk.d.ts +6 -0
  47. package/dist/context/ingest/adapters/sigma/chunk.js +119 -0
  48. package/dist/context/ingest/adapters/sigma/client-port.d.ts +45 -0
  49. package/dist/context/ingest/adapters/sigma/client-port.js +1 -0
  50. package/dist/context/ingest/adapters/sigma/client.d.ts +33 -0
  51. package/dist/context/ingest/adapters/sigma/client.js +176 -0
  52. package/dist/context/ingest/adapters/sigma/detect.d.ts +1 -0
  53. package/dist/context/ingest/adapters/sigma/detect.js +23 -0
  54. package/dist/context/ingest/adapters/sigma/fetch.d.ts +14 -0
  55. package/dist/context/ingest/adapters/sigma/fetch.js +191 -0
  56. package/dist/context/ingest/adapters/sigma/local-sigma.adapter.d.ts +17 -0
  57. package/dist/context/ingest/adapters/sigma/local-sigma.adapter.js +41 -0
  58. package/dist/context/ingest/adapters/sigma/project.d.ts +8 -0
  59. package/dist/context/ingest/adapters/sigma/project.js +186 -0
  60. package/dist/context/ingest/adapters/sigma/sigma.adapter.d.ts +18 -0
  61. package/dist/context/ingest/adapters/sigma/sigma.adapter.js +43 -0
  62. package/dist/context/ingest/adapters/sigma/types.d.ts +80 -0
  63. package/dist/context/ingest/adapters/sigma/types.js +82 -0
  64. package/dist/context/ingest/local-adapters.d.ts +2 -1
  65. package/dist/context/ingest/local-adapters.js +22 -0
  66. package/dist/context/mcp/context-tools.js +1 -0
  67. package/dist/context/mcp/local-project-ports.d.ts +1 -1
  68. package/dist/context/mcp/local-project-ports.js +53 -51
  69. package/dist/context/project/config.d.ts +122 -0
  70. package/dist/context/project/driver-schemas.d.ts +61 -0
  71. package/dist/context/project/driver-schemas.js +47 -0
  72. package/dist/context/scan/local-scan.js +3 -1
  73. package/dist/context/scan/types.d.ts +1 -1
  74. package/dist/context/sl/local-query.js +23 -6
  75. package/dist/context/sl/semantic-layer.service.d.ts +0 -4
  76. package/dist/context/sl/semantic-layer.service.js +3 -20
  77. package/dist/context/sl/tools/sl-warehouse-validation.d.ts +1 -1
  78. package/dist/context/sl/tools/sl-warehouse-validation.js +2 -2
  79. package/dist/context/sl/types.d.ts +1 -0
  80. package/dist/context/sql-analysis/dialect-notes.d.ts +1 -1
  81. package/dist/context/sql-analysis/dialect-notes.js +3 -2
  82. package/dist/context/sql-analysis/dialect.js +1 -0
  83. package/dist/context/sql-analysis/dialects/athena.md +12 -0
  84. package/dist/context/sql-analysis/dialects/duckdb.md +10 -0
  85. package/dist/local-adapters.js +17 -0
  86. package/dist/public-ingest.js +1 -0
  87. package/dist/setup-databases.d.ts +1 -1
  88. package/dist/setup-databases.js +52 -2
  89. package/dist/setup-sources.d.ts +2 -1
  90. package/dist/setup-sources.js +84 -0
  91. package/dist/skills/sigma_ingest/SKILL.md +189 -0
  92. package/dist/sql.js +9 -14
  93. package/dist/status-project.js +2 -1
  94. package/dist/telemetry/events.d.ts +1 -1
  95. package/package.json +3 -1
  96. package/assets/python/kaelio_ktx-0.14.0-py3-none-any.whl +0 -0
@@ -7,6 +7,7 @@ import { type LookerMappingClient, type LookerTableIdentifierParser } from './ad
7
7
  import type { LookerRuntimeClient } from './adapters/looker/fetch.js';
8
8
  import type { MetabaseClientLogger } from './adapters/metabase/client.js';
9
9
  import type { MetabaseFetchLogger } from './adapters/metabase/fetch.js';
10
+ import type { SigmaFetchLogger } from './adapters/sigma/fetch.js';
10
11
  import type { NotionFetchLogger } from './adapters/notion/fetch.js';
11
12
  import type { SourceAdapter } from './types.js';
12
13
  export interface DefaultLocalIngestAdaptersOptions {
@@ -29,7 +30,7 @@ export interface DefaultLocalIngestAdaptersOptions {
29
30
  };
30
31
  logger?: LocalIngestOperationalLogger;
31
32
  }
32
- type LocalIngestOperationalLogger = MetabaseClientLogger & MetabaseFetchLogger & LookerClientLogger & NotionFetchLogger;
33
+ type LocalIngestOperationalLogger = MetabaseClientLogger & MetabaseFetchLogger & LookerClientLogger & NotionFetchLogger & SigmaFetchLogger;
33
34
  export declare function createDefaultLocalIngestAdapters(project: KtxLocalProject, options?: DefaultLocalIngestAdaptersOptions): SourceAdapter[];
34
35
  export declare function localPullConfigForAdapter(project: KtxLocalProject, adapter: SourceAdapter, connectionId: string, options?: DefaultLocalIngestAdaptersOptions): Promise<unknown>;
35
36
  export {};
@@ -22,6 +22,7 @@ import { buildLookerPullConfigFromInputs, } from './adapters/looker/mapping.js';
22
22
  import { LookmlSourceAdapter } from './adapters/lookml/lookml.adapter.js';
23
23
  import { pullConfigFromIntegrationConfig } from './adapters/lookml/pull-config.js';
24
24
  import { createLocalMetabaseSourceAdapter } from './adapters/metabase/local-metabase.adapter.js';
25
+ import { createLocalSigmaSourceAdapter } from './adapters/sigma/local-sigma.adapter.js';
25
26
  import { MetricflowSourceAdapter } from './adapters/metricflow/metricflow.adapter.js';
26
27
  import { pullConfigFromMetricflowIntegration } from './adapters/metricflow/pull-config.js';
27
28
  import { LocalNotionRuntimeStore } from './adapters/notion/local-state-store.js';
@@ -51,6 +52,9 @@ export function createDefaultLocalIngestAdapters(project, options = {}) {
51
52
  createLocalMetabaseSourceAdapter(project, {
52
53
  ...(options.logger ? { logger: options.logger } : {}),
53
54
  }),
55
+ createLocalSigmaSourceAdapter(project, {
56
+ ...(options.logger ? { logger: options.logger } : {}),
57
+ }),
54
58
  new GdriveSourceAdapter(),
55
59
  new LookerSourceAdapter({
56
60
  clientFactory: {
@@ -183,6 +187,24 @@ export async function localPullConfigForAdapter(project, adapter, connectionId,
183
187
  if (adapter.source === 'metabase') {
184
188
  throw new Error('Metabase scheduled pulls fan out by mapping. Call runLocalMetabaseIngest() or use `ktx ingest <metabase-source-id>` from the CLI.');
185
189
  }
190
+ if (adapter.source === 'sigma') {
191
+ const sigmaConn = project.config.connections[connectionId];
192
+ const connectionMappings = sigmaConn && 'connectionMappings' in sigmaConn && sigmaConn.connectionMappings != null
193
+ ? sigmaConn.connectionMappings
194
+ : undefined;
195
+ const workbookFilter = sigmaConn && 'workbookFilter' in sigmaConn && sigmaConn.workbookFilter != null
196
+ ? sigmaConn.workbookFilter
197
+ : undefined;
198
+ const dataModelFilter = sigmaConn && 'dataModelFilter' in sigmaConn && sigmaConn.dataModelFilter != null
199
+ ? sigmaConn.dataModelFilter
200
+ : undefined;
201
+ return {
202
+ sigmaConnectionId: connectionId,
203
+ ...(connectionMappings ? { connectionMappings } : {}),
204
+ ...(workbookFilter ? { workbookFilter } : {}),
205
+ ...(dataModelFilter ? { dataModelFilter } : {}),
206
+ };
207
+ }
186
208
  const connection = project.config.connections[connectionId];
187
209
  if (adapter.source === HISTORIC_SQL_SOURCE_KEY) {
188
210
  if (options.historicSqlPullConfigOverride) {
@@ -171,6 +171,7 @@ const connectionListOutputSchema = z.object({
171
171
  connectionType: z.string(),
172
172
  members: z.array(z.string()).optional(),
173
173
  hint: z.string().optional(),
174
+ queryPolicy: z.literal('semantic-layer-only').optional(),
174
175
  })),
175
176
  });
176
177
  const wikiSearchOutputSchema = z.object({
@@ -1,7 +1,7 @@
1
1
  import type { KtxSqlQueryExecutorPort } from '../../context/connections/query-executor.js';
2
2
  import type { KtxProjectConnectionConfig } from '../../context/project/config.js';
3
3
  import type { KtxEmbeddingPort } from '../../context/core/embedding.js';
4
- import type { KtxSemanticLayerComputePort } from '../../context/daemon/semantic-layer-compute.js';
4
+ import { type KtxSemanticLayerComputePort } from '../../context/daemon/semantic-layer-compute.js';
5
5
  import type { KtxLocalProject } from '../../context/project/project.js';
6
6
  import type { LocalScanMcpOptions } from '../../context/scan/local-scan.js';
7
7
  import type { SqlAnalysisPort } from '../../context/sql-analysis/ports.js';
@@ -1,11 +1,11 @@
1
1
  import { KtxExpectedError, KtxQueryError, isNativeProgrammingFault } from '../../errors.js';
2
2
  import { isDatabaseDriver, normalizeConnectionDriver } from '../../connection-drivers.js';
3
3
  import { sqlDialectNotes } from '../../context/sql-analysis/dialect-notes.js';
4
- import { executeProjectReadOnlySql } from '../../context/connections/project-sql-executor.js';
5
- import { FEDERATED_CONNECTION_ID, federatedConnectionListing } from '../../context/connections/federation.js';
6
- import { assertSqlQueryableConnection } from '../../context/connections/dialects.js';
7
- import { resolveConfiguredConnection } from '../../context/connections/resolve-connection.js';
4
+ import { executeProjectRawSql } from '../../context/connections/project-sql-executor.js';
5
+ import { federatedConnectionListing } from '../../context/connections/federation.js';
6
+ import { projectAllowsRawSql, restrictedFederatedMemberIds } from '../../context/connections/query-policy.js';
8
7
  import { localConnectionInfoFromConfig, } from '../../context/connections/local-warehouse-descriptor.js';
8
+ import { KtxDaemonComputeError } from '../../context/daemon/semantic-layer-compute.js';
9
9
  import { createKtxEntityDetailsService } from '../../context/scan/entity-details.js';
10
10
  import { createKtxDiscoverDataService } from '../../context/search/discover.js';
11
11
  import { sqlAnalysisDialectForDriver } from '../../context/sql-analysis/dialect.js';
@@ -15,8 +15,26 @@ import { readLocalSlSource } from '../../context/sl/local-sl.js';
15
15
  import { assertSafeConnectionId } from '../../context/sl/source-files.js';
16
16
  import { assertConfiguredConnectionId } from '../../context/connections/configured-connections.js';
17
17
  import { readLocalKnowledgePage, searchLocalKnowledgePages } from '../wiki/local-knowledge.js';
18
+ /**
19
+ * Reclassify a query-path failure. Warehouse/driver rejections and daemon
20
+ * input-rejections are caller-driven outcomes (KtxQueryError, kept out of Error
21
+ * Tracking while preserving the underlying diagnostics); native JS faults, daemon
22
+ * crashes, and already-expected errors propagate unchanged so genuine ktx bugs
23
+ * still reach Error Tracking.
24
+ */
25
+ function throwClassifiedQueryError(error) {
26
+ if (error instanceof KtxDaemonComputeError) {
27
+ if (error.inputRejected) {
28
+ throw new KtxQueryError(error.detail, { cause: error });
29
+ }
30
+ throw error;
31
+ }
32
+ if (isNativeProgrammingFault(error) || error instanceof KtxExpectedError) {
33
+ throw error;
34
+ }
35
+ throw new KtxQueryError(error instanceof Error ? error.message : String(error), { cause: error });
36
+ }
18
37
  async function executeValidatedReadOnlySql(project, options, input, onProgress) {
19
- await onProgress?.({ progress: 0, message: 'Validating SQL' });
20
38
  if (!options.sqlAnalysis) {
21
39
  throw new Error('sql_execution requires parser-backed SQL validation.');
22
40
  }
@@ -24,50 +42,22 @@ async function executeValidatedReadOnlySql(project, options, input, onProgress)
24
42
  if (!createConnector) {
25
43
  throw new Error('sql_execution requires a local scan connector factory.');
26
44
  }
27
- const isFederated = input.connectionId === FEDERATED_CONNECTION_ID;
28
- const connectionId = isFederated ? input.connectionId : assertSafeConnectionId(input.connectionId);
29
- const connection = isFederated ? undefined : resolveConfiguredConnection(project.config, connectionId);
30
- if (!isFederated) {
31
- assertSqlQueryableConnection(connectionId, connection.driver);
32
- }
33
- const dialect = sqlAnalysisDialectForDriver(isFederated ? 'duckdb' : connection.driver);
34
- const validation = await options.sqlAnalysis.validateReadOnly(input.sql, dialect);
35
- if (!validation.ok) {
36
- // A read-only guard rejecting the agent's SQL is an expected outcome, not a
37
- // ktx fault: classify it so reportException keeps it out of Error Tracking.
38
- throw new KtxQueryError(validation.error ?? 'SQL is not read-only.');
39
- }
40
- await onProgress?.({ progress: 0.3, message: 'Executing' });
41
- const result = await executeProjectReadOnlySql({
45
+ const result = await executeProjectRawSql({
42
46
  project,
43
- input: {
44
- connectionId,
45
- projectDir: project.projectDir,
46
- connection,
47
- sql: input.sql,
48
- maxRows: input.maxRows,
49
- },
47
+ connectionId: input.connectionId,
48
+ sql: input.sql,
49
+ maxRows: input.maxRows,
50
+ sqlAnalysis: options.sqlAnalysis,
50
51
  createConnector,
51
52
  runId: 'mcp-sql-execution',
52
- }).catch((error) => {
53
- // A warehouse/driver rejection (e.g. the agent's SQL failed to compile) is a
54
- // surfaced operational outcome, not a ktx fault: mark it expected while
55
- // preserving the warehouse's own diagnostics. A native JS error (TypeError,
56
- // etc.) signals a bug in connector code — let it propagate unchanged so Error
57
- // Tracking still sees it.
58
- if (isNativeProgrammingFault(error) || error instanceof KtxExpectedError) {
59
- throw error;
60
- }
61
- throw new KtxQueryError(error instanceof Error ? error.message : String(error), { cause: error });
53
+ onProgress,
62
54
  });
63
- const response = {
55
+ return {
64
56
  headers: result.headers,
65
57
  ...(result.headerTypes ? { headerTypes: result.headerTypes } : {}),
66
58
  rows: result.rows,
67
59
  rowCount: result.rowCount ?? result.rows.length,
68
60
  };
69
- await onProgress?.({ progress: 1, message: `Fetched ${response.rowCount} rows` });
70
- return response;
71
61
  }
72
62
  /** @internal Resolves a connection's dialect SQL notes; throws KtxExpectedError for an unknown or non-SQL-warehouse connection. */
73
63
  export function resolveDialectNotesForConnection(connectionId, connection) {
@@ -92,12 +82,16 @@ export function createLocalProjectMcpContextPorts(project, options) {
92
82
  .sort((a, b) => a.id.localeCompare(b.id));
93
83
  const federated = federatedConnectionListing(project.config.connections, project.projectDir);
94
84
  if (federated) {
85
+ const restricted = restrictedFederatedMemberIds(project.config, project.projectDir);
95
86
  configured.push({
96
87
  id: federated.id,
97
88
  name: federated.id,
98
89
  connectionType: 'DUCKDB',
99
90
  members: federated.members,
100
- hint: federated.hint,
91
+ hint: restricted.length > 0
92
+ ? `Federated SQL is disabled: member connection(s) ${restricted.join(', ')} have query_policy: semantic-layer-only.`
93
+ : federated.hint,
94
+ ...(restricted.length > 0 ? { queryPolicy: 'semantic-layer-only' } : {}),
101
95
  });
102
96
  }
103
97
  return configured;
@@ -158,15 +152,20 @@ export function createLocalProjectMcpContextPorts(project, options) {
158
152
  if (!options.semanticLayerCompute) {
159
153
  throw new Error('sl_query requires a semantic-layer query adapter.');
160
154
  }
161
- return compileLocalSlQuery(project, {
162
- connectionId: input.connectionId,
163
- query: input.query,
164
- compute: options.semanticLayerCompute,
165
- execute: Boolean(options.queryExecutor),
166
- maxRows: input.query.limit,
167
- queryExecutor: options.queryExecutor,
168
- onProgress: executionOptions?.onProgress,
169
- });
155
+ try {
156
+ return await compileLocalSlQuery(project, {
157
+ connectionId: input.connectionId,
158
+ query: input.query,
159
+ compute: options.semanticLayerCompute,
160
+ execute: Boolean(options.queryExecutor),
161
+ maxRows: input.query.limit,
162
+ queryExecutor: options.queryExecutor,
163
+ onProgress: executionOptions?.onProgress,
164
+ });
165
+ }
166
+ catch (error) {
167
+ throwClassifiedQueryError(error);
168
+ }
170
169
  },
171
170
  },
172
171
  entityDetails: {
@@ -191,7 +190,10 @@ export function createLocalProjectMcpContextPorts(project, options) {
191
190
  },
192
191
  },
193
192
  };
194
- if (options.sqlAnalysis && options.localScan?.createConnector) {
193
+ // Register sql_execution only when some connection can accept raw SQL; in
194
+ // mixed projects the tool stays and executeProjectRawSql rejects restricted
195
+ // connection ids at request time.
196
+ if (options.sqlAnalysis && options.localScan?.createConnector && projectAllowsRawSql(project.config)) {
195
197
  ports.sqlExecution = {
196
198
  async execute(input, executionOptions) {
197
199
  return executeValidatedReadOnlySql(project, options, input, executionOptions?.onProgress);
@@ -109,36 +109,82 @@ declare const connectionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
109
109
  url: z.ZodOptional<z.ZodString>;
110
110
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
111
111
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
112
+ query_policy: z.ZodOptional<z.ZodEnum<{
113
+ "read-only-sql": "read-only-sql";
114
+ "semantic-layer-only": "semantic-layer-only";
115
+ }>>;
112
116
  }, z.core.$loose>, z.ZodObject<{
113
117
  driver: z.ZodLiteral<"mysql">;
114
118
  url: z.ZodOptional<z.ZodString>;
115
119
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
116
120
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
121
+ query_policy: z.ZodOptional<z.ZodEnum<{
122
+ "read-only-sql": "read-only-sql";
123
+ "semantic-layer-only": "semantic-layer-only";
124
+ }>>;
117
125
  }, z.core.$loose>, z.ZodObject<{
118
126
  driver: z.ZodLiteral<"snowflake">;
119
127
  url: z.ZodOptional<z.ZodString>;
120
128
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
121
129
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
130
+ query_policy: z.ZodOptional<z.ZodEnum<{
131
+ "read-only-sql": "read-only-sql";
132
+ "semantic-layer-only": "semantic-layer-only";
133
+ }>>;
122
134
  }, z.core.$loose>, z.ZodObject<{
123
135
  driver: z.ZodLiteral<"bigquery">;
124
136
  url: z.ZodOptional<z.ZodString>;
125
137
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
126
138
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
139
+ query_policy: z.ZodOptional<z.ZodEnum<{
140
+ "read-only-sql": "read-only-sql";
141
+ "semantic-layer-only": "semantic-layer-only";
142
+ }>>;
127
143
  }, z.core.$loose>, z.ZodObject<{
128
144
  driver: z.ZodLiteral<"sqlite">;
129
145
  url: z.ZodOptional<z.ZodString>;
130
146
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
131
147
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
148
+ query_policy: z.ZodOptional<z.ZodEnum<{
149
+ "read-only-sql": "read-only-sql";
150
+ "semantic-layer-only": "semantic-layer-only";
151
+ }>>;
152
+ }, z.core.$loose>, z.ZodObject<{
153
+ driver: z.ZodLiteral<"duckdb">;
154
+ url: z.ZodOptional<z.ZodString>;
155
+ enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
156
+ query_timeout_ms: z.ZodOptional<z.ZodNumber>;
157
+ query_policy: z.ZodOptional<z.ZodEnum<{
158
+ "read-only-sql": "read-only-sql";
159
+ "semantic-layer-only": "semantic-layer-only";
160
+ }>>;
132
161
  }, z.core.$loose>, z.ZodObject<{
133
162
  driver: z.ZodLiteral<"clickhouse">;
134
163
  url: z.ZodOptional<z.ZodString>;
135
164
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
136
165
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
166
+ query_policy: z.ZodOptional<z.ZodEnum<{
167
+ "read-only-sql": "read-only-sql";
168
+ "semantic-layer-only": "semantic-layer-only";
169
+ }>>;
137
170
  }, z.core.$loose>, z.ZodObject<{
138
171
  driver: z.ZodLiteral<"sqlserver">;
139
172
  url: z.ZodOptional<z.ZodString>;
140
173
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
141
174
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
175
+ query_policy: z.ZodOptional<z.ZodEnum<{
176
+ "read-only-sql": "read-only-sql";
177
+ "semantic-layer-only": "semantic-layer-only";
178
+ }>>;
179
+ }, z.core.$loose>, z.ZodObject<{
180
+ driver: z.ZodLiteral<"athena">;
181
+ url: z.ZodOptional<z.ZodString>;
182
+ enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
183
+ query_timeout_ms: z.ZodOptional<z.ZodNumber>;
184
+ query_policy: z.ZodOptional<z.ZodEnum<{
185
+ "read-only-sql": "read-only-sql";
186
+ "semantic-layer-only": "semantic-layer-only";
187
+ }>>;
142
188
  }, z.core.$loose>, z.ZodObject<{
143
189
  driver: z.ZodLiteral<"mongodb">;
144
190
  url: z.ZodString;
@@ -223,6 +269,21 @@ declare const connectionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
223
269
  path: z.ZodOptional<z.ZodString>;
224
270
  auth_token_ref: z.ZodOptional<z.ZodString>;
225
271
  }, z.core.$loose>;
272
+ }, z.core.$loose>, z.ZodObject<{
273
+ driver: z.ZodLiteral<"sigma">;
274
+ api_url: z.ZodDefault<z.ZodString>;
275
+ client_id: z.ZodString;
276
+ client_secret: z.ZodOptional<z.ZodString>;
277
+ client_secret_ref: z.ZodOptional<z.ZodString>;
278
+ connectionMappings: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
279
+ workbookFilter: z.ZodOptional<z.ZodObject<{
280
+ includeArchived: z.ZodDefault<z.ZodBoolean>;
281
+ includeExplorations: z.ZodDefault<z.ZodBoolean>;
282
+ updatedSince: z.ZodOptional<z.ZodString>;
283
+ }, z.core.$strip>>;
284
+ dataModelFilter: z.ZodOptional<z.ZodObject<{
285
+ updatedSince: z.ZodOptional<z.ZodString>;
286
+ }, z.core.$strip>>;
226
287
  }, z.core.$loose>], "driver">;
227
288
  declare const ktxProjectConfigSchema: z.ZodObject<{
228
289
  setup: z.ZodOptional<z.ZodObject<{
@@ -233,36 +294,82 @@ declare const ktxProjectConfigSchema: z.ZodObject<{
233
294
  url: z.ZodOptional<z.ZodString>;
234
295
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
235
296
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
297
+ query_policy: z.ZodOptional<z.ZodEnum<{
298
+ "read-only-sql": "read-only-sql";
299
+ "semantic-layer-only": "semantic-layer-only";
300
+ }>>;
236
301
  }, z.core.$loose>, z.ZodObject<{
237
302
  driver: z.ZodLiteral<"mysql">;
238
303
  url: z.ZodOptional<z.ZodString>;
239
304
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
240
305
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
306
+ query_policy: z.ZodOptional<z.ZodEnum<{
307
+ "read-only-sql": "read-only-sql";
308
+ "semantic-layer-only": "semantic-layer-only";
309
+ }>>;
241
310
  }, z.core.$loose>, z.ZodObject<{
242
311
  driver: z.ZodLiteral<"snowflake">;
243
312
  url: z.ZodOptional<z.ZodString>;
244
313
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
245
314
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
315
+ query_policy: z.ZodOptional<z.ZodEnum<{
316
+ "read-only-sql": "read-only-sql";
317
+ "semantic-layer-only": "semantic-layer-only";
318
+ }>>;
246
319
  }, z.core.$loose>, z.ZodObject<{
247
320
  driver: z.ZodLiteral<"bigquery">;
248
321
  url: z.ZodOptional<z.ZodString>;
249
322
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
250
323
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
324
+ query_policy: z.ZodOptional<z.ZodEnum<{
325
+ "read-only-sql": "read-only-sql";
326
+ "semantic-layer-only": "semantic-layer-only";
327
+ }>>;
251
328
  }, z.core.$loose>, z.ZodObject<{
252
329
  driver: z.ZodLiteral<"sqlite">;
253
330
  url: z.ZodOptional<z.ZodString>;
254
331
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
255
332
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
333
+ query_policy: z.ZodOptional<z.ZodEnum<{
334
+ "read-only-sql": "read-only-sql";
335
+ "semantic-layer-only": "semantic-layer-only";
336
+ }>>;
337
+ }, z.core.$loose>, z.ZodObject<{
338
+ driver: z.ZodLiteral<"duckdb">;
339
+ url: z.ZodOptional<z.ZodString>;
340
+ enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
341
+ query_timeout_ms: z.ZodOptional<z.ZodNumber>;
342
+ query_policy: z.ZodOptional<z.ZodEnum<{
343
+ "read-only-sql": "read-only-sql";
344
+ "semantic-layer-only": "semantic-layer-only";
345
+ }>>;
256
346
  }, z.core.$loose>, z.ZodObject<{
257
347
  driver: z.ZodLiteral<"clickhouse">;
258
348
  url: z.ZodOptional<z.ZodString>;
259
349
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
260
350
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
351
+ query_policy: z.ZodOptional<z.ZodEnum<{
352
+ "read-only-sql": "read-only-sql";
353
+ "semantic-layer-only": "semantic-layer-only";
354
+ }>>;
261
355
  }, z.core.$loose>, z.ZodObject<{
262
356
  driver: z.ZodLiteral<"sqlserver">;
263
357
  url: z.ZodOptional<z.ZodString>;
264
358
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
265
359
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
360
+ query_policy: z.ZodOptional<z.ZodEnum<{
361
+ "read-only-sql": "read-only-sql";
362
+ "semantic-layer-only": "semantic-layer-only";
363
+ }>>;
364
+ }, z.core.$loose>, z.ZodObject<{
365
+ driver: z.ZodLiteral<"athena">;
366
+ url: z.ZodOptional<z.ZodString>;
367
+ enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
368
+ query_timeout_ms: z.ZodOptional<z.ZodNumber>;
369
+ query_policy: z.ZodOptional<z.ZodEnum<{
370
+ "read-only-sql": "read-only-sql";
371
+ "semantic-layer-only": "semantic-layer-only";
372
+ }>>;
266
373
  }, z.core.$loose>, z.ZodObject<{
267
374
  driver: z.ZodLiteral<"mongodb">;
268
375
  url: z.ZodString;
@@ -347,6 +454,21 @@ declare const ktxProjectConfigSchema: z.ZodObject<{
347
454
  path: z.ZodOptional<z.ZodString>;
348
455
  auth_token_ref: z.ZodOptional<z.ZodString>;
349
456
  }, z.core.$loose>;
457
+ }, z.core.$loose>, z.ZodObject<{
458
+ driver: z.ZodLiteral<"sigma">;
459
+ api_url: z.ZodDefault<z.ZodString>;
460
+ client_id: z.ZodString;
461
+ client_secret: z.ZodOptional<z.ZodString>;
462
+ client_secret_ref: z.ZodOptional<z.ZodString>;
463
+ connectionMappings: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
464
+ workbookFilter: z.ZodOptional<z.ZodObject<{
465
+ includeArchived: z.ZodDefault<z.ZodBoolean>;
466
+ includeExplorations: z.ZodDefault<z.ZodBoolean>;
467
+ updatedSince: z.ZodOptional<z.ZodString>;
468
+ }, z.core.$strip>>;
469
+ dataModelFilter: z.ZodOptional<z.ZodObject<{
470
+ updatedSince: z.ZodOptional<z.ZodString>;
471
+ }, z.core.$strip>>;
350
472
  }, z.core.$loose>], "driver">>>;
351
473
  storage: z.ZodPrefault<z.ZodObject<{
352
474
  state: z.ZodDefault<z.ZodEnum<{
@@ -4,36 +4,82 @@ export declare const connectionConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObjec
4
4
  url: z.ZodOptional<z.ZodString>;
5
5
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
6
6
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
7
+ query_policy: z.ZodOptional<z.ZodEnum<{
8
+ "read-only-sql": "read-only-sql";
9
+ "semantic-layer-only": "semantic-layer-only";
10
+ }>>;
7
11
  }, z.core.$loose>, z.ZodObject<{
8
12
  driver: z.ZodLiteral<"mysql">;
9
13
  url: z.ZodOptional<z.ZodString>;
10
14
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
11
15
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
16
+ query_policy: z.ZodOptional<z.ZodEnum<{
17
+ "read-only-sql": "read-only-sql";
18
+ "semantic-layer-only": "semantic-layer-only";
19
+ }>>;
12
20
  }, z.core.$loose>, z.ZodObject<{
13
21
  driver: z.ZodLiteral<"snowflake">;
14
22
  url: z.ZodOptional<z.ZodString>;
15
23
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
16
24
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
25
+ query_policy: z.ZodOptional<z.ZodEnum<{
26
+ "read-only-sql": "read-only-sql";
27
+ "semantic-layer-only": "semantic-layer-only";
28
+ }>>;
17
29
  }, z.core.$loose>, z.ZodObject<{
18
30
  driver: z.ZodLiteral<"bigquery">;
19
31
  url: z.ZodOptional<z.ZodString>;
20
32
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
21
33
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
34
+ query_policy: z.ZodOptional<z.ZodEnum<{
35
+ "read-only-sql": "read-only-sql";
36
+ "semantic-layer-only": "semantic-layer-only";
37
+ }>>;
22
38
  }, z.core.$loose>, z.ZodObject<{
23
39
  driver: z.ZodLiteral<"sqlite">;
24
40
  url: z.ZodOptional<z.ZodString>;
25
41
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
26
42
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
43
+ query_policy: z.ZodOptional<z.ZodEnum<{
44
+ "read-only-sql": "read-only-sql";
45
+ "semantic-layer-only": "semantic-layer-only";
46
+ }>>;
47
+ }, z.core.$loose>, z.ZodObject<{
48
+ driver: z.ZodLiteral<"duckdb">;
49
+ url: z.ZodOptional<z.ZodString>;
50
+ enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
51
+ query_timeout_ms: z.ZodOptional<z.ZodNumber>;
52
+ query_policy: z.ZodOptional<z.ZodEnum<{
53
+ "read-only-sql": "read-only-sql";
54
+ "semantic-layer-only": "semantic-layer-only";
55
+ }>>;
27
56
  }, z.core.$loose>, z.ZodObject<{
28
57
  driver: z.ZodLiteral<"clickhouse">;
29
58
  url: z.ZodOptional<z.ZodString>;
30
59
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
31
60
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
61
+ query_policy: z.ZodOptional<z.ZodEnum<{
62
+ "read-only-sql": "read-only-sql";
63
+ "semantic-layer-only": "semantic-layer-only";
64
+ }>>;
32
65
  }, z.core.$loose>, z.ZodObject<{
33
66
  driver: z.ZodLiteral<"sqlserver">;
34
67
  url: z.ZodOptional<z.ZodString>;
35
68
  enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
36
69
  query_timeout_ms: z.ZodOptional<z.ZodNumber>;
70
+ query_policy: z.ZodOptional<z.ZodEnum<{
71
+ "read-only-sql": "read-only-sql";
72
+ "semantic-layer-only": "semantic-layer-only";
73
+ }>>;
74
+ }, z.core.$loose>, z.ZodObject<{
75
+ driver: z.ZodLiteral<"athena">;
76
+ url: z.ZodOptional<z.ZodString>;
77
+ enabled_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
78
+ query_timeout_ms: z.ZodOptional<z.ZodNumber>;
79
+ query_policy: z.ZodOptional<z.ZodEnum<{
80
+ "read-only-sql": "read-only-sql";
81
+ "semantic-layer-only": "semantic-layer-only";
82
+ }>>;
37
83
  }, z.core.$loose>, z.ZodObject<{
38
84
  driver: z.ZodLiteral<"mongodb">;
39
85
  url: z.ZodString;
@@ -118,4 +164,19 @@ export declare const connectionConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObjec
118
164
  path: z.ZodOptional<z.ZodString>;
119
165
  auth_token_ref: z.ZodOptional<z.ZodString>;
120
166
  }, z.core.$loose>;
167
+ }, z.core.$loose>, z.ZodObject<{
168
+ driver: z.ZodLiteral<"sigma">;
169
+ api_url: z.ZodDefault<z.ZodString>;
170
+ client_id: z.ZodString;
171
+ client_secret: z.ZodOptional<z.ZodString>;
172
+ client_secret_ref: z.ZodOptional<z.ZodString>;
173
+ connectionMappings: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
174
+ workbookFilter: z.ZodOptional<z.ZodObject<{
175
+ includeArchived: z.ZodDefault<z.ZodBoolean>;
176
+ includeExplorations: z.ZodDefault<z.ZodBoolean>;
177
+ updatedSince: z.ZodOptional<z.ZodString>;
178
+ }, z.core.$strip>>;
179
+ dataModelFilter: z.ZodOptional<z.ZodObject<{
180
+ updatedSince: z.ZodOptional<z.ZodString>;
181
+ }, z.core.$strip>>;
121
182
  }, z.core.$loose>], "driver">;
@@ -6,8 +6,10 @@ const warehouseDrivers = [
6
6
  'snowflake',
7
7
  'bigquery',
8
8
  'sqlite',
9
+ 'duckdb',
9
10
  'clickhouse',
10
11
  'sqlserver',
12
+ 'athena',
11
13
  ];
12
14
  function warehouseConnectionSchema(driver) {
13
15
  return z
@@ -28,6 +30,10 @@ function warehouseConnectionSchema(driver) {
28
30
  .positive()
29
31
  .optional()
30
32
  .describe('Maximum execution time for a single read-only query, in milliseconds (default 30000). Enforced as a server-side statement timeout for remote engines and by SIGKILL-ing a forked query subprocess for in-process SQLite. A query exceeding it is cancelled and returns a "query exceeded Ns" error so the agent can revise.'),
33
+ query_policy: z
34
+ .enum(['read-only-sql', 'semantic-layer-only'])
35
+ .optional()
36
+ .describe('Agent-facing query authorship policy (default "read-only-sql"). "read-only-sql" allows parser-validated read-only SQL plus semantic-layer queries. "semantic-layer-only" rejects raw SQL on this connection (`ktx sql`, the sql_execution tool, and federated queries that include it) and restricts semantic-layer queries to measures predefined in the semantic-layer sources. ktx-internal scan and ingest queries are unaffected.'),
31
37
  })
32
38
  .describe(`${driver} warehouse connection. Additional driver-tunable fields (e.g. context.queryHistory) are accepted and passed through.`);
33
39
  }
@@ -37,8 +43,10 @@ const warehouseConnectionSchemas = [
37
43
  warehouseConnectionSchema('snowflake'),
38
44
  warehouseConnectionSchema('bigquery'),
39
45
  warehouseConnectionSchema('sqlite'),
46
+ warehouseConnectionSchema('duckdb'),
40
47
  warehouseConnectionSchema('clickhouse'),
41
48
  warehouseConnectionSchema('sqlserver'),
49
+ warehouseConnectionSchema('athena'),
42
50
  ];
43
51
  const mongodbConnectionSchema = z
44
52
  .looseObject({
@@ -218,6 +226,44 @@ const metricflowConnectionSchema = z
218
226
  .describe('Nested MetricFlow configuration block.'),
219
227
  })
220
228
  .describe('MetricFlow / SL context-source connection.');
229
+ const sigmaConnectionSchema = z
230
+ .looseObject({
231
+ driver: z.literal('sigma'),
232
+ api_url: z
233
+ .string()
234
+ .url()
235
+ .default('https://api.sigmacomputing.com')
236
+ .describe('Sigma API base URL. Defaults to the GCP US endpoint; change for other regions.'),
237
+ client_id: z.string().min(1).describe('Sigma API client ID.'),
238
+ client_secret: z.string().min(1).optional().describe('Literal Sigma client secret. Prefer client_secret_ref.'),
239
+ client_secret_ref: z
240
+ .string()
241
+ .min(1)
242
+ .optional()
243
+ .describe('Reference to Sigma client secret (e.g. env:SIGMA_CLIENT_SECRET).'),
244
+ connectionMappings: z
245
+ .record(z.string(), z.string())
246
+ .optional()
247
+ .describe('Maps Sigma internal connection UUIDs to ktx warehouse connection IDs. ' +
248
+ 'When set, projected semantic-layer sources land under the mapped warehouse connection ' +
249
+ 'instead of the Sigma connection, enabling sl_validate. ' +
250
+ 'Find UUIDs in data model specs under source.connectionId.'),
251
+ workbookFilter: z
252
+ .object({
253
+ includeArchived: z.boolean().default(false),
254
+ includeExplorations: z.boolean().default(false),
255
+ updatedSince: z.string().optional().describe('ISO 8601 date string. Only workbooks updated on or after this date are ingested.'),
256
+ })
257
+ .optional()
258
+ .describe('Filters applied when listing workbooks during ingest. Defaults exclude archived and exploration workbooks.'),
259
+ dataModelFilter: z
260
+ .object({
261
+ updatedSince: z.string().optional().describe('ISO 8601 date string. Only data models updated on or after this date are fetched.'),
262
+ })
263
+ .optional()
264
+ .describe('Filters applied when listing data models during ingest.'),
265
+ })
266
+ .describe('Sigma Computing API connection for ingesting data models.');
221
267
  export const connectionConfigSchema = z.discriminatedUnion('driver', [
222
268
  ...warehouseConnectionSchemas,
223
269
  mongodbConnectionSchema,
@@ -228,4 +274,5 @@ export const connectionConfigSchema = z.discriminatedUnion('driver', [
228
274
  gdriveConnectionSchema,
229
275
  dbtConnectionSchema,
230
276
  metricflowConnectionSchema,
277
+ sigmaConnectionSchema,
231
278
  ]);
@@ -43,15 +43,17 @@ function normalizeDriver(driver) {
43
43
  const normalized = (driver ?? '').toLowerCase();
44
44
  if (normalized === 'postgres' ||
45
45
  normalized === 'sqlite' ||
46
+ normalized === 'duckdb' ||
46
47
  normalized === 'mysql' ||
47
48
  normalized === 'clickhouse' ||
48
49
  normalized === 'sqlserver' ||
49
50
  normalized === 'bigquery' ||
50
51
  normalized === 'snowflake' ||
52
+ normalized === 'athena' ||
51
53
  normalized === 'mongodb') {
52
54
  return normalized;
53
55
  }
54
- throw new Error(`Standalone ktx scan supports postgres/sqlite/mysql/clickhouse/sqlserver/bigquery/snowflake/mongodb in this phase, received "${driver ?? 'unknown'}"`);
56
+ throw new Error(`Standalone ktx scan supports postgres/sqlite/duckdb/mysql/clickhouse/sqlserver/bigquery/snowflake/athena/mongodb in this phase, received "${driver ?? 'unknown'}"`);
55
57
  }
56
58
  function tablePathCount(paths) {
57
59
  return paths.filter((path) => path.startsWith('tables/') && path.endsWith('.json')).length;