@kaelio/ktx 0.8.0 → 0.9.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 (83) hide show
  1. package/assets/python/{kaelio_ktx-0.8.0-py3-none-any.whl → kaelio_ktx-0.9.0-py3-none-any.whl} +0 -0
  2. package/assets/python/manifest.json +4 -4
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/cli-runtime.js +50 -3
  5. package/dist/commands/setup-commands.js +1 -1
  6. package/dist/connection-recovery.d.ts +34 -0
  7. package/dist/connection-recovery.js +82 -0
  8. package/dist/connection.js +3 -1
  9. package/dist/context/ingest/adapters/historic-sql/bigquery-query-history-reader.js +71 -20
  10. package/dist/context/ingest/adapters/historic-sql/chunk-unified.js +2 -1
  11. package/dist/context/ingest/adapters/historic-sql/connection-dialect.d.ts +9 -0
  12. package/dist/context/ingest/adapters/historic-sql/connection-dialect.js +15 -4
  13. package/dist/context/ingest/adapters/historic-sql/pattern-inputs.js +8 -2
  14. package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.d.ts +29 -0
  15. package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.js +190 -0
  16. package/dist/context/ingest/adapters/historic-sql/scope-floor.d.ts +18 -0
  17. package/dist/context/ingest/adapters/historic-sql/scope-floor.js +229 -0
  18. package/dist/context/ingest/adapters/historic-sql/scope-membership.d.ts +8 -0
  19. package/dist/context/ingest/adapters/historic-sql/scope-membership.js +29 -0
  20. package/dist/context/ingest/adapters/historic-sql/snowflake-query-history-reader.js +68 -19
  21. package/dist/context/ingest/adapters/historic-sql/stage-unified.js +57 -50
  22. package/dist/context/ingest/adapters/historic-sql/types.d.ts +36 -3
  23. package/dist/context/ingest/adapters/historic-sql/types.js +14 -2
  24. package/dist/context/ingest/context-evidence/sqlite-context-evidence-store.d.ts +1 -1
  25. package/dist/context/ingest/isolated-diff/patch-integrator.js +75 -5
  26. package/dist/context/ingest/local-adapters.js +21 -4
  27. package/dist/context/ingest/local-bundle-runtime.js +3 -2
  28. package/dist/context/llm/codex-exec-events.d.ts +20 -0
  29. package/dist/context/llm/codex-exec-events.js +155 -0
  30. package/dist/context/llm/codex-isolation.d.ts +3 -0
  31. package/dist/context/llm/codex-isolation.js +5 -0
  32. package/dist/context/llm/codex-mcp-runtime-server.d.ts +24 -0
  33. package/dist/context/llm/codex-mcp-runtime-server.js +51 -0
  34. package/dist/context/llm/codex-models.d.ts +2 -0
  35. package/dist/context/llm/codex-models.js +17 -0
  36. package/dist/context/llm/codex-runtime-config.d.ts +16 -0
  37. package/dist/context/llm/codex-runtime-config.js +19 -0
  38. package/dist/context/llm/codex-runtime.d.ts +37 -0
  39. package/dist/context/llm/codex-runtime.js +304 -0
  40. package/dist/context/llm/codex-sdk-runner.d.ts +21 -0
  41. package/dist/context/llm/codex-sdk-runner.js +63 -0
  42. package/dist/context/llm/local-config.d.ts +2 -0
  43. package/dist/context/llm/local-config.js +12 -1
  44. package/dist/context/project/config.d.ts +2 -0
  45. package/dist/context/project/config.js +2 -2
  46. package/dist/context/sql-analysis/http-sql-analysis-port.js +32 -2
  47. package/dist/context/sql-analysis/ports.d.ts +12 -2
  48. package/dist/context/tools/context-candidate-mark.tool.d.ts +2 -2
  49. package/dist/context-build-view.js +4 -32
  50. package/dist/io/buffered-command-io.d.ts +11 -0
  51. package/dist/io/buffered-command-io.js +28 -0
  52. package/dist/llm/types.d.ts +1 -1
  53. package/dist/local-adapters.d.ts +10 -2
  54. package/dist/local-adapters.js +19 -3
  55. package/dist/next-steps.js +1 -2
  56. package/dist/progress-port-adapter.d.ts +6 -0
  57. package/dist/progress-port-adapter.js +18 -0
  58. package/dist/public-ingest.d.ts +20 -1
  59. package/dist/public-ingest.js +178 -27
  60. package/dist/scan.js +3 -1
  61. package/dist/setup-context.d.ts +2 -0
  62. package/dist/setup-context.js +133 -27
  63. package/dist/setup-databases.d.ts +17 -1
  64. package/dist/setup-databases.js +358 -249
  65. package/dist/setup-models.d.ts +10 -1
  66. package/dist/setup-models.js +90 -2
  67. package/dist/setup-ready-menu.d.ts +16 -2
  68. package/dist/setup-ready-menu.js +37 -5
  69. package/dist/setup-sources.js +108 -28
  70. package/dist/setup.js +22 -10
  71. package/dist/status-project.d.ts +11 -0
  72. package/dist/status-project.js +50 -1
  73. package/dist/telemetry/command-hook.d.ts +1 -0
  74. package/dist/telemetry/command-hook.js +3 -1
  75. package/dist/telemetry/events.d.ts +11 -6
  76. package/dist/telemetry/events.js +10 -2
  77. package/dist/telemetry/identity.d.ts +0 -1
  78. package/dist/telemetry/identity.js +6 -6
  79. package/dist/telemetry/index.d.ts +12 -0
  80. package/dist/telemetry/index.js +13 -2
  81. package/dist/telemetry/scrubber.d.ts +10 -0
  82. package/dist/telemetry/scrubber.js +20 -0
  83. package/package.json +5 -4
@@ -5,7 +5,10 @@ import { loadKtxProject } from './context/project/project.js';
5
5
  import { markKtxSetupStateStepComplete, readKtxSetupState } from './context/project/setup-config.js';
6
6
  import { serializeKtxProjectConfig } from './context/project/config.js';
7
7
  import { errorMessage, writePrefixedLines } from './clack.js';
8
+ import { formatErrorDetail } from './telemetry/scrubber.js';
8
9
  import { buildPublicIngestPlan } from './public-ingest.js';
10
+ import { runKtxConnection } from './connection.js';
11
+ import { createBufferedCommandIo } from './io/buffered-command-io.js';
9
12
  import { runContextBuild, } from './context-build-view.js';
10
13
  import { createKtxSetupPromptAdapter, } from './setup-prompts.js';
11
14
  const SETUP_CONTEXT_STATE_PATH = ['.ktx', 'setup', 'context-build.json'];
@@ -169,6 +172,98 @@ function listContextTargets(project) {
169
172
  .map((target) => target.connectionId),
170
173
  };
171
174
  }
175
+ function requiredConnectionIds(targets) {
176
+ return [...targets.primarySourceConnectionIds, ...targets.contextSourceConnectionIds];
177
+ }
178
+ function connectorTypeLabel(project, connectionId) {
179
+ const driver = String(project.config.connections[connectionId]?.driver ?? '')
180
+ .trim()
181
+ .toLowerCase();
182
+ return driver.length > 0 ? driver : 'unknown';
183
+ }
184
+ async function defaultGateTestConnection(projectDir, connectionId, io) {
185
+ return await runKtxConnection({ command: 'test', projectDir, connectionId }, io);
186
+ }
187
+ /**
188
+ * Runs a live connection test for every connection the build depends on. Each
189
+ * test's output is captured in a buffer and discarded so raw error text never
190
+ * reaches the user — callers surface only the connection id and connector type.
191
+ */
192
+ async function testRequiredConnections(projectDir, project, targets, testConnection) {
193
+ const failures = [];
194
+ for (const connectionId of requiredConnectionIds(targets)) {
195
+ const buffered = createBufferedCommandIo();
196
+ const exitCode = await testConnection(projectDir, connectionId, buffered);
197
+ if (exitCode !== 0) {
198
+ failures.push({ connectionId, driver: connectorTypeLabel(project, connectionId) });
199
+ }
200
+ }
201
+ return failures.length === 0 ? { ok: true } : { ok: false, failures };
202
+ }
203
+ /**
204
+ * Loads the project and resolves the connections the build depends on, applying
205
+ * the empty-targets and preflight-capability checks. Used both on first entry
206
+ * and on interactive retry so a fix that adds, removes, or reconfigures a
207
+ * connection is honored.
208
+ */
209
+ async function prepareBuildTargets(args, io) {
210
+ const project = await loadKtxProject({ projectDir: args.projectDir });
211
+ const targets = listContextTargets(project);
212
+ if (targets.primarySourceConnectionIds.length === 0 && targets.contextSourceConnectionIds.length === 0) {
213
+ if (args.allowEmpty === true) {
214
+ return { kind: 'result', result: { status: 'skipped', projectDir: args.projectDir } };
215
+ }
216
+ io.stderr.write('No databases or context sources are configured for a KTX context build.\n');
217
+ return { kind: 'result', result: { status: 'failed', projectDir: args.projectDir } };
218
+ }
219
+ const preflightPlan = buildPublicIngestPlan(project, { projectDir: project.projectDir, all: true });
220
+ const preflightFailures = preflightPlan.targets.flatMap((target) => target.preflightFailure ? [`${target.connectionId}: ${target.preflightFailure}`] : []);
221
+ if (preflightFailures.length > 0) {
222
+ if (args.allowEmpty === true) {
223
+ return { kind: 'result', result: { status: 'skipped', projectDir: args.projectDir } };
224
+ }
225
+ writeMissingCapabilities(preflightFailures, io);
226
+ return { kind: 'result', result: { status: 'missing-input', projectDir: args.projectDir } };
227
+ }
228
+ return { kind: 'ready', project, targets };
229
+ }
230
+ function writeConnectionGateFailureLines(io, projectDir, failures) {
231
+ io.stderr.write('KTX cannot build context: a required connection failed its live test.\n\n');
232
+ io.stderr.write('Failed connections:\n');
233
+ for (const failure of failures) {
234
+ io.stderr.write(` ${failure.connectionId} (${failure.driver})\n`);
235
+ }
236
+ io.stderr.write('\nEach connection must be reachable before KTX builds context.\n');
237
+ io.stderr.write(`Run \`ktx connection test <id> --project-dir ${resolve(projectDir)}\` to see the error, fix the connection, then retry.\n`);
238
+ }
239
+ function connectionGateFailureReason(failures) {
240
+ const names = failures.map((failure) => `${failure.connectionId} (${failure.driver})`).join(', ');
241
+ return `Required connections failed their live test: ${names}.`;
242
+ }
243
+ async function writeConnectionGateFailedState(args, deps, targets, failures) {
244
+ const at = (deps.now ?? (() => new Date()))().toISOString();
245
+ await writeKtxSetupContextState(args.projectDir, {
246
+ status: 'failed',
247
+ startedAt: at,
248
+ updatedAt: at,
249
+ primarySourceConnectionIds: targets.primarySourceConnectionIds,
250
+ contextSourceConnectionIds: targets.contextSourceConnectionIds,
251
+ reportIds: [],
252
+ artifactPaths: [],
253
+ retryableFailedTargets: [],
254
+ commands: contextBuildCommands(args.projectDir),
255
+ failureReason: connectionGateFailureReason(failures),
256
+ });
257
+ }
258
+ async function promptConnectionGateRetry(prompts) {
259
+ return (await prompts.select({
260
+ message: 'Fix the failing connection, then choose how to proceed.',
261
+ options: [
262
+ { value: 'retry', label: 'Retry connection tests' },
263
+ { value: 'back', label: 'Back' },
264
+ ],
265
+ }));
266
+ }
172
267
  async function hasFileWithExtension(root, extensions, options = {}) {
173
268
  if (!(await pathExists(root))) {
174
269
  return false;
@@ -303,12 +398,10 @@ function writeMissingCapabilities(missing, io) {
303
398
  }
304
399
  io.stderr.write('\nFix this in setup before building context.\n');
305
400
  }
306
- function writeSkippedContext(projectDir, io) {
307
- io.stdout.write('\nKTX is configured, but context has not been built yet.\n\n');
308
- io.stdout.write('Agents were not connected because KTX has not prepared searchable context for them.\n\n');
309
- io.stdout.write(`Resume setup:\n ktx setup --project-dir ${resolve(projectDir)}\n\n`);
310
- io.stdout.write(`Build context:\n ktx setup --project-dir ${resolve(projectDir)}\n\n`);
311
- io.stdout.write(`Check status:\n ktx status --project-dir ${resolve(projectDir)}\n`);
401
+ function writeSkippedContext(io) {
402
+ // The setup completion screen owns "what to do next" (it points at `ktx ingest`),
403
+ // so keep this to a short acknowledgement rather than a competing command list.
404
+ io.stdout.write('\nLeaving context unbuilt for now.\n');
312
405
  }
313
406
  function writeSuccess(readiness, targets, io) {
314
407
  io.stdout.write('\nKTX context is ready for agents.\n\n');
@@ -473,7 +566,6 @@ async function completeExistingContext(args, io, deps, targets) {
473
566
  }
474
567
  export async function runKtxSetupContextStep(args, io, deps = {}) {
475
568
  try {
476
- const project = await loadKtxProject({ projectDir: args.projectDir });
477
569
  const prompts = deps.prompts ?? createPromptAdapter();
478
570
  const existingState = await readKtxSetupContextState(args.projectDir);
479
571
  const completedSteps = (await readKtxSetupState(args.projectDir)).completed_steps;
@@ -487,43 +579,57 @@ export async function runKtxSetupContextStep(args, io, deps = {}) {
487
579
  if (existingState.status === 'stale') {
488
580
  io.stdout.write('Previous context build state is stale; starting a fresh foreground build.\n');
489
581
  }
490
- const targets = listContextTargets(project);
491
- if (targets.primarySourceConnectionIds.length === 0 && targets.contextSourceConnectionIds.length === 0) {
492
- if (args.allowEmpty === true) {
493
- return { status: 'skipped', projectDir: args.projectDir };
494
- }
495
- io.stderr.write('No databases or context sources are configured for a KTX context build.\n');
496
- return { status: 'failed', projectDir: args.projectDir };
497
- }
498
- const preflightPlan = buildPublicIngestPlan(project, { projectDir: project.projectDir, all: true });
499
- const preflightFailures = preflightPlan.targets.flatMap((target) => target.preflightFailure ? [`${target.connectionId}: ${target.preflightFailure}`] : []);
500
- if (preflightFailures.length > 0) {
501
- if (args.allowEmpty === true) {
502
- return { status: 'skipped', projectDir: args.projectDir };
503
- }
504
- writeMissingCapabilities(preflightFailures, io);
505
- return { status: 'missing-input', projectDir: args.projectDir };
582
+ const prepared = await prepareBuildTargets(args, io);
583
+ if (prepared.kind === 'result') {
584
+ return prepared.result;
506
585
  }
586
+ let { project, targets } = prepared;
587
+ const interactive = args.inputMode !== 'disabled' && args.prompt !== false;
507
588
  if (args.forcePrompt !== true && args.prompt !== false && deps.verifyContextReady === undefined) {
508
589
  const existingContextResult = await completeExistingContext(args, io, deps, targets);
509
590
  if (existingContextResult) {
510
591
  return existingContextResult;
511
592
  }
512
593
  }
513
- if (args.inputMode !== 'disabled' && args.prompt !== false) {
594
+ if (interactive) {
514
595
  const choice = await promptForBuild(prompts);
515
596
  if (choice === 'back') {
516
597
  return { status: 'back', projectDir: args.projectDir };
517
598
  }
518
599
  if (choice === 'skip') {
519
- writeSkippedContext(args.projectDir, io);
600
+ writeSkippedContext(io);
520
601
  return { status: 'skipped', projectDir: args.projectDir };
521
602
  }
522
603
  }
523
- return await runBuild(args, io, deps, project, targets);
604
+ // Live-connection gate: every connection the build depends on must pass a
605
+ // live test before the (expensive) build starts. A red connection is a hard
606
+ // stop — we surface only the connection id and connector type, never raw
607
+ // error text.
608
+ const testConnection = deps.testConnection ?? defaultGateTestConnection;
609
+ while (true) {
610
+ const gate = await testRequiredConnections(args.projectDir, project, targets, testConnection);
611
+ if (gate.ok) {
612
+ return await runBuild(args, io, deps, project, targets);
613
+ }
614
+ writeConnectionGateFailureLines(io, args.projectDir, gate.failures);
615
+ if (!interactive) {
616
+ await writeConnectionGateFailedState(args, deps, targets, gate.failures);
617
+ return { status: 'failed', projectDir: args.projectDir };
618
+ }
619
+ const choice = await promptConnectionGateRetry(prompts);
620
+ if (choice === 'back') {
621
+ return { status: 'back', projectDir: args.projectDir };
622
+ }
623
+ const reprepared = await prepareBuildTargets(args, io);
624
+ if (reprepared.kind === 'result') {
625
+ return reprepared.result;
626
+ }
627
+ project = reprepared.project;
628
+ targets = reprepared.targets;
629
+ }
524
630
  }
525
631
  catch (error) {
526
632
  writePrefixedLines((chunk) => io.stderr.write(chunk), errorMessage(error));
527
- return { status: 'failed', projectDir: args.projectDir };
633
+ return { status: 'failed', projectDir: args.projectDir, errorDetail: formatErrorDetail(error) };
528
634
  }
529
635
  }
@@ -1,12 +1,20 @@
1
+ import type { KtxLlmRuntimePort } from './context/llm/runtime-port.js';
2
+ import { type ProposeQueryHistoryServiceAccountFiltersInput, type QueryHistoryFilterProposal } from './context/ingest/adapters/historic-sql/query-history-filter-picker.js';
1
3
  import { type HistoricSqlReadinessProbe } from './context/ingest/historic-sql-probes.js';
4
+ import { loadKtxProject } from './context/project/project.js';
2
5
  import type { KtxTableListEntry } from './context/scan/types.js';
3
- import type { KtxCliIo } from './cli-runtime.js';
6
+ import { type KtxCliIo } from './cli-runtime.js';
4
7
  import { type DatabaseScopePickResult, type PickDatabaseScopeArgs } from './database-tree-picker.js';
8
+ import type { KtxManagedPythonInstallPolicy } from './managed-python-command.js';
9
+ import type { ManagedPythonCoreDaemonOptions } from './managed-python-http.js';
5
10
  import { type KtxSetupPromptOption } from './setup-prompts.js';
6
11
  export type KtxSetupDatabaseDriver = 'sqlite' | 'postgres' | 'mysql' | 'clickhouse' | 'sqlserver' | 'bigquery' | 'snowflake';
7
12
  export interface KtxSetupDatabasesArgs {
8
13
  projectDir: string;
9
14
  inputMode: 'auto' | 'disabled';
15
+ yes?: boolean;
16
+ cliVersion?: string;
17
+ runtimeInstallPolicy?: KtxManagedPythonInstallPolicy;
10
18
  databaseDrivers?: KtxSetupDatabaseDriver[];
11
19
  databaseConnectionIds?: string[];
12
20
  databaseConnectionId?: string;
@@ -77,5 +85,13 @@ export interface KtxSetupDatabasesDeps {
77
85
  listTables?: (projectDir: string, connectionId: string, schemas?: string[]) => Promise<KtxTableListEntry[]>;
78
86
  pickDatabaseScope?: (args: PickDatabaseScopeArgs, io: KtxCliIo) => Promise<DatabaseScopePickResult>;
79
87
  historicSqlReadinessProbe?: HistoricSqlReadinessProbe;
88
+ queryHistoryFilterPicker?: (input: ProposeQueryHistoryServiceAccountFiltersInput) => Promise<QueryHistoryFilterProposal>;
89
+ createQueryHistoryLlmRuntime?: (projectDir: string, project: Awaited<ReturnType<typeof loadKtxProject>>) => KtxLlmRuntimePort | null;
80
90
  }
91
+ /** @internal */
92
+ export declare function managedDaemonOptionsForSetupQueryHistoryPicker(input: {
93
+ projectDir: string;
94
+ args: Pick<KtxSetupDatabasesArgs, 'cliVersion' | 'runtimeInstallPolicy' | 'inputMode'>;
95
+ io: KtxCliIo;
96
+ }): ManagedPythonCoreDaemonOptions;
81
97
  export declare function runKtxSetupDatabasesStep(args: KtxSetupDatabasesArgs, io: KtxCliIo, deps?: KtxSetupDatabasesDeps): Promise<KtxSetupDatabasesResult>;