@kaelio/ktx 0.7.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 (142) hide show
  1. package/assets/python/{kaelio_ktx-0.7.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-program.js +7 -0
  5. package/dist/cli-runtime.js +50 -3
  6. package/dist/command-schemas.d.ts +1 -1
  7. package/dist/command-tree.js +5 -1
  8. package/dist/commands/completion-commands.d.ts +3 -0
  9. package/dist/commands/completion-commands.js +38 -0
  10. package/dist/commands/ingest-commands.js +0 -4
  11. package/dist/commands/knowledge-commands.js +15 -2
  12. package/dist/commands/setup-commands.js +3 -3
  13. package/dist/commands/sl-commands.js +19 -7
  14. package/dist/completion/complete-engine.d.ts +19 -0
  15. package/dist/completion/complete-engine.js +128 -0
  16. package/dist/completion/completion-scripts.d.ts +1 -0
  17. package/dist/completion/completion-scripts.js +36 -0
  18. package/dist/completion/dynamic-candidates.d.ts +6 -0
  19. package/dist/completion/dynamic-candidates.js +98 -0
  20. package/dist/connection-drivers.d.ts +3 -0
  21. package/dist/connection-drivers.js +17 -0
  22. package/dist/connection-recovery.d.ts +34 -0
  23. package/dist/connection-recovery.js +82 -0
  24. package/dist/connection.js +3 -1
  25. package/dist/context/ingest/adapters/historic-sql/bigquery-query-history-reader.js +71 -20
  26. package/dist/context/ingest/adapters/historic-sql/chunk-unified.js +2 -1
  27. package/dist/context/ingest/adapters/historic-sql/connection-dialect.d.ts +9 -0
  28. package/dist/context/ingest/adapters/historic-sql/connection-dialect.js +15 -4
  29. package/dist/context/ingest/adapters/historic-sql/pattern-inputs.js +8 -2
  30. package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.d.ts +29 -0
  31. package/dist/context/ingest/adapters/historic-sql/query-history-filter-picker.js +190 -0
  32. package/dist/context/ingest/adapters/historic-sql/scope-floor.d.ts +18 -0
  33. package/dist/context/ingest/adapters/historic-sql/scope-floor.js +229 -0
  34. package/dist/context/ingest/adapters/historic-sql/scope-membership.d.ts +8 -0
  35. package/dist/context/ingest/adapters/historic-sql/scope-membership.js +29 -0
  36. package/dist/context/ingest/adapters/historic-sql/snowflake-query-history-reader.js +68 -19
  37. package/dist/context/ingest/adapters/historic-sql/stage-unified.js +57 -50
  38. package/dist/context/ingest/adapters/historic-sql/types.d.ts +36 -3
  39. package/dist/context/ingest/adapters/historic-sql/types.js +14 -2
  40. package/dist/context/ingest/context-evidence/sqlite-context-evidence-store.d.ts +1 -1
  41. package/dist/context/ingest/ingest-bundle.runner.d.ts +8 -0
  42. package/dist/context/ingest/ingest-bundle.runner.js +72 -15
  43. package/dist/context/ingest/ingest-profile.d.ts +102 -0
  44. package/dist/context/ingest/ingest-profile.js +306 -0
  45. package/dist/context/ingest/isolated-diff/patch-integrator.js +75 -5
  46. package/dist/context/ingest/isolated-diff/work-unit-executor.js +25 -2
  47. package/dist/context/ingest/local-adapters.js +21 -4
  48. package/dist/context/ingest/local-bundle-runtime.js +4 -2
  49. package/dist/context/ingest/local-ingest.d.ts +1 -1
  50. package/dist/context/ingest/local-ingest.js +6 -4
  51. package/dist/context/ingest/memory-flow/events.js +2 -1
  52. package/dist/context/ingest/ports.d.ts +2 -0
  53. package/dist/context/ingest/reports.d.ts +3 -0
  54. package/dist/context/ingest/reports.js +10 -0
  55. package/dist/context/ingest/stages/stage-3-work-units.d.ts +3 -1
  56. package/dist/context/ingest/stages/stage-3-work-units.js +2 -0
  57. package/dist/context/ingest/stages/stage-4-reconciliation.d.ts +2 -1
  58. package/dist/context/ingest/stages/stage-4-reconciliation.js +1 -1
  59. package/dist/context/ingest/tools/tool-call-logger.d.ts +6 -0
  60. package/dist/context/ingest/tools/tool-call-logger.js +36 -1
  61. package/dist/context/llm/ai-sdk-runtime.js +32 -3
  62. package/dist/context/llm/claude-code-runtime.js +35 -2
  63. package/dist/context/llm/codex-exec-events.d.ts +20 -0
  64. package/dist/context/llm/codex-exec-events.js +155 -0
  65. package/dist/context/llm/codex-isolation.d.ts +3 -0
  66. package/dist/context/llm/codex-isolation.js +5 -0
  67. package/dist/context/llm/codex-mcp-runtime-server.d.ts +24 -0
  68. package/dist/context/llm/codex-mcp-runtime-server.js +51 -0
  69. package/dist/context/llm/codex-models.d.ts +2 -0
  70. package/dist/context/llm/codex-models.js +17 -0
  71. package/dist/context/llm/codex-runtime-config.d.ts +16 -0
  72. package/dist/context/llm/codex-runtime-config.js +19 -0
  73. package/dist/context/llm/codex-runtime.d.ts +37 -0
  74. package/dist/context/llm/codex-runtime.js +304 -0
  75. package/dist/context/llm/codex-sdk-runner.d.ts +21 -0
  76. package/dist/context/llm/codex-sdk-runner.js +63 -0
  77. package/dist/context/llm/local-config.d.ts +2 -0
  78. package/dist/context/llm/local-config.js +12 -1
  79. package/dist/context/llm/runtime-port.d.ts +25 -0
  80. package/dist/context/mcp/context-tools.d.ts +2 -1
  81. package/dist/context/mcp/context-tools.js +82 -15
  82. package/dist/context/mcp/server.js +4 -0
  83. package/dist/context/mcp/types.d.ts +15 -1
  84. package/dist/context/project/config.d.ts +3 -0
  85. package/dist/context/project/config.js +6 -2
  86. package/dist/context/project/driver-schemas.js +1 -1
  87. package/dist/context/search/discover.js +4 -3
  88. package/dist/context/sl/local-sl.d.ts +15 -0
  89. package/dist/context/sl/local-sl.js +30 -0
  90. package/dist/context/sql-analysis/http-sql-analysis-port.js +32 -2
  91. package/dist/context/sql-analysis/ports.d.ts +12 -2
  92. package/dist/context/tools/context-candidate-mark.tool.d.ts +2 -2
  93. package/dist/context/wiki/local-knowledge.d.ts +10 -0
  94. package/dist/context/wiki/local-knowledge.js +22 -0
  95. package/dist/context-build-view.d.ts +0 -3
  96. package/dist/context-build-view.js +5 -39
  97. package/dist/ingest.js +7 -10
  98. package/dist/io/buffered-command-io.d.ts +11 -0
  99. package/dist/io/buffered-command-io.js +28 -0
  100. package/dist/knowledge.d.ts +5 -0
  101. package/dist/knowledge.js +10 -1
  102. package/dist/llm/types.d.ts +1 -1
  103. package/dist/local-adapters.d.ts +10 -2
  104. package/dist/local-adapters.js +19 -3
  105. package/dist/next-steps.js +1 -2
  106. package/dist/progress-port-adapter.d.ts +6 -0
  107. package/dist/progress-port-adapter.js +18 -0
  108. package/dist/public-ingest-copy.js +1 -1
  109. package/dist/public-ingest.d.ts +20 -8
  110. package/dist/public-ingest.js +198 -61
  111. package/dist/scan.js +3 -1
  112. package/dist/setup-context.d.ts +2 -0
  113. package/dist/setup-context.js +138 -64
  114. package/dist/setup-databases.d.ts +17 -1
  115. package/dist/setup-databases.js +366 -326
  116. package/dist/setup-models.d.ts +10 -1
  117. package/dist/setup-models.js +90 -2
  118. package/dist/setup-ready-menu.d.ts +16 -2
  119. package/dist/setup-ready-menu.js +37 -5
  120. package/dist/setup-sources.js +141 -33
  121. package/dist/setup.js +24 -12
  122. package/dist/skills/analytics/SKILL.md +6 -1
  123. package/dist/sl.d.ts +6 -1
  124. package/dist/sl.js +32 -8
  125. package/dist/status-project.d.ts +11 -0
  126. package/dist/status-project.js +50 -1
  127. package/dist/telemetry/command-hook.d.ts +1 -0
  128. package/dist/telemetry/command-hook.js +3 -1
  129. package/dist/telemetry/emitter.js +1 -1
  130. package/dist/telemetry/events.d.ts +15 -9
  131. package/dist/telemetry/events.js +17 -5
  132. package/dist/telemetry/identity.d.ts +1 -2
  133. package/dist/telemetry/identity.js +13 -10
  134. package/dist/telemetry/index.d.ts +13 -1
  135. package/dist/telemetry/index.js +18 -3
  136. package/dist/telemetry/scrubber.d.ts +10 -0
  137. package/dist/telemetry/scrubber.js +20 -0
  138. package/package.json +20 -19
  139. package/dist/ingest-depth.d.ts +0 -8
  140. package/dist/ingest-depth.js +0 -56
  141. package/dist/setup-database-context-depth.d.ts +0 -23
  142. package/dist/setup-database-context-depth.js +0 -84
@@ -59,6 +59,13 @@ function optionalString(raw, field) {
59
59
  }
60
60
  throw new Error(`sql analysis response has invalid optional string field ${field}`);
61
61
  }
62
+ function optionalNullableStringField(raw, field) {
63
+ const value = raw[field];
64
+ if (value === null || value === undefined || typeof value === 'string') {
65
+ return value ?? null;
66
+ }
67
+ throw new Error(`sql analysis response has invalid optional nullable string field ${field}`);
68
+ }
62
69
  function requiredStringArray(raw, field) {
63
70
  const value = raw[field];
64
71
  if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) {
@@ -136,10 +143,32 @@ function mapColumnsByClause(raw) {
136
143
  }
137
144
  return result;
138
145
  }
146
+ function requiredTableRef(raw, field) {
147
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
148
+ throw new Error(`sql analysis response contains invalid table ref in ${field}`);
149
+ }
150
+ const record = raw;
151
+ const name = record.name;
152
+ if (typeof name !== 'string' || name.length === 0) {
153
+ throw new Error(`sql analysis response table ref in ${field} is missing name`);
154
+ }
155
+ return {
156
+ catalog: optionalNullableStringField(record, 'catalog'),
157
+ db: optionalNullableStringField(record, 'db'),
158
+ name,
159
+ };
160
+ }
161
+ function requiredTableRefArray(raw, field) {
162
+ const value = raw[field];
163
+ if (!Array.isArray(value)) {
164
+ throw new Error(`sql analysis response is missing table-ref[] field ${field}`);
165
+ }
166
+ return value.map((item, index) => requiredTableRef(item, `${field}.${index}`));
167
+ }
139
168
  function mapBatchResult(raw) {
140
169
  const error = optionalString(raw, 'error');
141
170
  return {
142
- tablesTouched: requiredStringArray(raw, 'tables_touched'),
171
+ tablesTouched: requiredTableRefArray(raw, 'tables_touched'),
143
172
  columnsByClause: mapColumnsByClause(raw),
144
173
  ...(error !== undefined ? { error } : {}),
145
174
  };
@@ -170,10 +199,11 @@ export function createHttpSqlAnalysisPort(options) {
170
199
  });
171
200
  return mapResult(raw);
172
201
  },
173
- async analyzeBatch(items, dialect) {
202
+ async analyzeBatch(items, dialect, options) {
174
203
  const raw = await requestJson('/sql/analyze-batch', {
175
204
  dialect,
176
205
  items,
206
+ ...(options?.catalog ? { catalog: options.catalog } : {}),
177
207
  });
178
208
  return mapBatchResponse(raw);
179
209
  },
@@ -1,3 +1,4 @@
1
+ import type { KtxTableRef } from '../scan/types.js';
1
2
  export type SqlAnalysisDialect = 'bigquery' | 'snowflake' | 'postgres' | 'redshift' | 'mysql' | 'sqlite' | 'tsql' | 'clickhouse' | (string & {});
2
3
  export type SqlAnalysisLiteralSlotType = 'string' | 'number' | 'timestamp' | 'date' | 'boolean' | 'null' | 'unknown';
3
4
  export interface SqlAnalysisLiteralSlot {
@@ -17,8 +18,17 @@ export interface SqlAnalysisBatchItem {
17
18
  id: string;
18
19
  sql: string;
19
20
  }
21
+ interface SqlAnalysisCatalogTable extends KtxTableRef {
22
+ columns?: string[];
23
+ }
24
+ interface SqlAnalysisCatalog {
25
+ tables: SqlAnalysisCatalogTable[];
26
+ }
27
+ export interface SqlAnalysisBatchOptions {
28
+ catalog?: SqlAnalysisCatalog;
29
+ }
20
30
  export interface SqlAnalysisBatchResult {
21
- tablesTouched: string[];
31
+ tablesTouched: KtxTableRef[];
22
32
  columnsByClause: Partial<Record<SqlAnalysisClause, string[]>>;
23
33
  error?: string | null;
24
34
  }
@@ -28,7 +38,7 @@ export interface SqlReadOnlyValidationResult {
28
38
  }
29
39
  export interface SqlAnalysisPort {
30
40
  analyzeForFingerprint(sql: string, dialect: SqlAnalysisDialect): Promise<SqlAnalysisFingerprintResult>;
31
- analyzeBatch(items: SqlAnalysisBatchItem[], dialect: SqlAnalysisDialect): Promise<Map<string, SqlAnalysisBatchResult>>;
41
+ analyzeBatch(items: SqlAnalysisBatchItem[], dialect: SqlAnalysisDialect, options?: SqlAnalysisBatchOptions): Promise<Map<string, SqlAnalysisBatchResult>>;
32
42
  validateReadOnly(sql: string, dialect: SqlAnalysisDialect): Promise<SqlReadOnlyValidationResult>;
33
43
  }
34
44
  export {};
@@ -8,8 +8,8 @@ declare const contextCandidateMarkInputSchema: z.ZodObject<{
8
8
  conflict: "conflict";
9
9
  merged: "merged";
10
10
  pending: "pending";
11
- promoted: "promoted";
12
11
  rejected: "rejected";
12
+ promoted: "promoted";
13
13
  }>;
14
14
  rejectionReason: z.ZodDefault<z.ZodNullable<z.ZodString>>;
15
15
  }, z.core.$strip>;
@@ -31,8 +31,8 @@ export declare class ContextCandidateMarkTool extends BaseTool<typeof contextCan
31
31
  conflict: "conflict";
32
32
  merged: "merged";
33
33
  pending: "pending";
34
- promoted: "promoted";
35
34
  rejected: "rejected";
35
+ promoted: "promoted";
36
36
  }>;
37
37
  rejectionReason: z.ZodDefault<z.ZodNullable<z.ZodString>>;
38
38
  }, z.core.$strip>;
@@ -50,6 +50,16 @@ export declare function readLocalKnowledgePage(project: KtxLocalProject, input:
50
50
  export declare function listLocalKnowledgePages(project: KtxLocalProject, input?: {
51
51
  userId?: string;
52
52
  }): Promise<LocalKnowledgeSummary[]>;
53
+ /**
54
+ * List wiki page keys without reading or parsing file contents.
55
+ *
56
+ * Keys are derived purely from file paths, so this stays cheap enough for
57
+ * shell tab-completion (unlike `listLocalKnowledgePages`, which reads every
58
+ * page to populate summaries).
59
+ */
60
+ export declare function listLocalKnowledgePageKeys(project: KtxLocalProject, input?: {
61
+ userId?: string;
62
+ }): Promise<string[]>;
53
63
  export declare function searchLocalKnowledgePages(project: KtxLocalProject, input: {
54
64
  query: string;
55
65
  userId?: string;
@@ -118,6 +118,28 @@ export async function listLocalKnowledgePages(project, input = {}) {
118
118
  }
119
119
  return pages.sort((left, right) => left.path.localeCompare(right.path));
120
120
  }
121
+ /**
122
+ * List wiki page keys without reading or parsing file contents.
123
+ *
124
+ * Keys are derived purely from file paths, so this stays cheap enough for
125
+ * shell tab-completion (unlike `listLocalKnowledgePages`, which reads every
126
+ * page to populate summaries).
127
+ */
128
+ export async function listLocalKnowledgePageKeys(project, input = {}) {
129
+ const userId = input.userId ?? 'local';
130
+ const keys = new Set();
131
+ for (const scope of ['GLOBAL', 'USER']) {
132
+ const root = scope === 'GLOBAL' ? 'wiki/global' : `wiki/user/${assertSafePathToken('user id', userId)}`;
133
+ const listed = await project.fileStore.listFiles(root);
134
+ for (const path of listed.files.filter((file) => file.endsWith('.md'))) {
135
+ const key = keyFromKnowledgePath(path, scope, userId);
136
+ if (key) {
137
+ keys.add(key);
138
+ }
139
+ }
140
+ }
141
+ return [...keys].sort();
142
+ }
121
143
  function scorePage(page, terms) {
122
144
  const haystack = buildKnowledgeSearchText(page.key, page.summary, page.content, page.tags).toLowerCase();
123
145
  return terms.some((term) => haystack.includes(term)) ? 3 : 0;
@@ -39,9 +39,6 @@ export interface ContextBuildArgs {
39
39
  targetConnectionId?: string;
40
40
  all?: boolean;
41
41
  entrypoint?: 'setup' | 'ingest';
42
- depth?: Extract<KtxPublicIngestArgs, {
43
- command: 'run';
44
- }>['depth'];
45
42
  queryHistory?: Extract<KtxPublicIngestArgs, {
46
43
  command: 'run';
47
44
  }>['queryHistory'];
@@ -1,5 +1,5 @@
1
- import { publicDatabaseIngestMessage, publicQueryHistoryMessage } from './public-ingest-copy.js';
2
- import { buildPublicIngestPlan, executePublicIngestTarget } from './public-ingest.js';
1
+ import { buildPublicIngestPlan, executePublicIngestTarget, publicProgressMessage } from './public-ingest.js';
2
+ import { createAggregateProgressPort } from './progress-port-adapter.js';
3
3
  import { formatDuration } from './demo-metrics.js';
4
4
  import { profileMark } from './startup-profile.js';
5
5
  profileMark('module:context-build-view');
@@ -241,12 +241,11 @@ function renderMessageGroup(label, messages, styled) {
241
241
  function retryCommand(input) {
242
242
  const projectPart = input.projectDir ? ` --project-dir ${input.projectDir}` : '';
243
243
  if (input.entrypoint === 'ingest' && input.connectionId) {
244
- const depthPart = input.depth ? ` --${input.depth}` : '';
245
244
  const queryHistoryPart = input.queryHistory ? ' --query-history' : '';
246
245
  const windowPart = input.queryHistory && input.queryHistoryWindowDays !== undefined
247
246
  ? ` --query-history-window-days ${input.queryHistoryWindowDays}`
248
247
  : '';
249
- return `ktx ingest ${input.connectionId}${projectPart}${depthPart}${queryHistoryPart}${windowPart}`;
248
+ return `ktx ingest ${input.connectionId}${projectPart}${queryHistoryPart}${windowPart}`;
250
249
  }
251
250
  return input.projectDir ? `ktx setup --project-dir ${input.projectDir}` : 'ktx setup';
252
251
  }
@@ -563,7 +562,6 @@ function appendRetryIfNeeded(input) {
563
562
  projectDir: input.projectDir,
564
563
  entrypoint: input.entrypoint,
565
564
  connectionId: input.target.connectionId,
566
- depth: input.target.databaseDepth,
567
565
  queryHistory: input.target.queryHistory?.enabled === true,
568
566
  queryHistoryWindowDays: input.target.queryHistory?.windowDays,
569
567
  })}`;
@@ -578,7 +576,6 @@ function failureTextForTarget(input) {
578
576
  projectDir: input.projectDir,
579
577
  entrypoint: input.entrypoint,
580
578
  connectionId: input.target.connectionId,
581
- depth: input.target.databaseDepth,
582
579
  queryHistory: input.target.queryHistory?.enabled === true,
583
580
  queryHistoryWindowDays: input.target.queryHistory?.windowDays,
584
581
  })}`,
@@ -593,7 +590,6 @@ function failureTextForTarget(input) {
593
590
  projectDir: input.projectDir,
594
591
  entrypoint: input.entrypoint,
595
592
  connectionId: input.target.connectionId,
596
- depth: input.target.databaseDepth,
597
593
  queryHistory: input.target.queryHistory?.enabled === true,
598
594
  queryHistoryWindowDays: input.target.queryHistory?.windowDays,
599
595
  })}`,
@@ -622,44 +618,15 @@ export function initViewState(targets) {
622
618
  totalElapsedMs: 0,
623
619
  };
624
620
  }
625
- function publicProgressMessage(message, target) {
626
- let current = message;
627
- if (target.operation === 'database-ingest') {
628
- current = publicDatabaseIngestMessage(current);
629
- }
630
- if (target.steps.includes('query-history')) {
631
- current = publicQueryHistoryMessage(current, target.connectionId);
632
- }
633
- return current;
634
- }
635
621
  function formatProgressDetail(update, target) {
636
622
  const percent = Math.max(0, Math.min(100, Math.round(update.percent)));
637
623
  return `[${percent}%] ${publicProgressMessage(update.message, target)}`;
638
624
  }
639
- function createContextBuildProgressPort(onProgress, state = { progress: 0 }, start = 0, weight = 1) {
640
- return {
641
- async update(value, message, options) {
642
- const absoluteValue = start + Math.max(0, Math.min(1, value)) * weight;
643
- state.progress = Math.max(state.progress, Math.min(1, absoluteValue));
644
- if (!message)
645
- return;
646
- onProgress({
647
- percent: Math.max(0, Math.min(100, Math.round(state.progress * 100))),
648
- message,
649
- ...(options?.transient !== undefined ? { transient: options.transient } : {}),
650
- });
651
- },
652
- startPhase(phaseWeight) {
653
- return createContextBuildProgressPort(onProgress, state, state.progress, weight * phaseWeight);
654
- },
655
- };
656
- }
657
625
  export async function runContextBuild(project, args, io, deps = {}) {
658
626
  const plan = buildPublicIngestPlan(project, {
659
627
  projectDir: args.projectDir,
660
628
  ...(args.targetConnectionId ? { targetConnectionId: args.targetConnectionId } : {}),
661
629
  all: args.all ?? true,
662
- ...(args.depth ? { depth: args.depth } : {}),
663
630
  ...(args.queryHistory ? { queryHistory: args.queryHistory } : {}),
664
631
  ...(args.queryHistoryWindowDays !== undefined ? { queryHistoryWindowDays: args.queryHistoryWindowDays } : {}),
665
632
  ...(args.scanMode ? { scanMode: args.scanMode } : {}),
@@ -721,7 +688,6 @@ export async function runContextBuild(project, args, io, deps = {}) {
721
688
  all: args.all ?? true,
722
689
  json: false,
723
690
  inputMode: args.inputMode,
724
- ...(args.depth ? { depth: args.depth } : {}),
725
691
  ...(args.queryHistory ? { queryHistory: args.queryHistory } : {}),
726
692
  ...(args.queryHistoryWindowDays !== undefined ? { queryHistoryWindowDays: args.queryHistoryWindowDays } : {}),
727
693
  ...(args.scanMode ? { scanMode: args.scanMode } : {}),
@@ -808,7 +774,7 @@ export async function runContextBuild(project, args, io, deps = {}) {
808
774
  hasPendingProgressPublish = !publishSourceProgress(false);
809
775
  };
810
776
  const progressDeps = {
811
- scanProgress: createContextBuildProgressPort(updateSchemaPhase),
777
+ scanProgress: createAggregateProgressPort(updateSchemaPhase),
812
778
  ingestProgress: updateIngestPhase,
813
779
  runtimeIo: io,
814
780
  onPhaseStart,
@@ -817,7 +783,7 @@ export async function runContextBuild(project, args, io, deps = {}) {
817
783
  let result = null;
818
784
  let thrownError = null;
819
785
  try {
820
- result = await execTarget(targetState.target, runArgs, capture.io, progressDeps);
786
+ result = await execTarget(targetState.target, runArgs, capture.io, progressDeps, project);
821
787
  }
822
788
  catch (error) {
823
789
  thrownError = error;
package/dist/ingest.js CHANGED
@@ -2,7 +2,7 @@ import { buildMemoryFlowViewModel } from './context/ingest/memory-flow/view-mode
2
2
  import { createMemoryFlowLiveBuffer, sanitizeMemoryFlowError } from './context/ingest/memory-flow/live-buffer.js';
3
3
  import { formatMemoryFlowFinalSummary } from './context/ingest/memory-flow/summary.js';
4
4
  import { getLatestLocalIngestStatus, getLocalIngestStatus, runLocalIngest, runLocalMetabaseIngest } from './context/ingest/local-ingest.js';
5
- import { savedMemoryCountsForReport } from './context/ingest/reports.js';
5
+ import { ingestReportOutcome, savedMemoryCountsForReport } from './context/ingest/reports.js';
6
6
  import { ingestReportToMemoryFlowReplay } from './context/ingest/memory-flow/events.js';
7
7
  import { renderMemoryFlowReplay } from './context/ingest/memory-flow/render.js';
8
8
  import { loadKtxProject } from './context/project/project.js';
@@ -17,9 +17,6 @@ import { renderMemoryFlowTui, startLiveMemoryFlowTui, } from './memory-flow-tui.
17
17
  import { resolveVizFallback, warnVizFallbackOnce } from './viz-fallback.js';
18
18
  import { profileMark } from './startup-profile.js';
19
19
  profileMark('module:ingest');
20
- function reportStatus(report) {
21
- return report.body.status === 'failed' || report.body.failedWorkUnits.length > 0 ? 'error' : 'done';
22
- }
23
20
  const REPORT_SOURCE_LABELS = new Map([
24
21
  ['live-database', 'Database schema'],
25
22
  ['historic-sql', 'Query history'],
@@ -106,7 +103,7 @@ function writeReportStatus(report, io) {
106
103
  if (report.body.tracePath) {
107
104
  io.stdout.write(`Trace: ${report.body.tracePath}\n`);
108
105
  }
109
- io.stdout.write(`Status: ${reportStatus(report)}\n`);
106
+ io.stdout.write(`Status: ${ingestReportOutcome(report)}\n`);
110
107
  io.stdout.write(`Source: ${reportSourceLabel(report.sourceKey)}\n`);
111
108
  io.stdout.write(`Connection: ${report.connectionId}\n`);
112
109
  io.stdout.write(`Sync: ${report.body.syncId}\n`);
@@ -138,7 +135,7 @@ function writeMetabaseFanoutStatus(result, io) {
138
135
  }
139
136
  io.stdout.write(`Saved memory: ${counts.wikiCount} wiki, ${counts.slCount} SL\n`);
140
137
  for (const child of result.children) {
141
- const status = reportStatus(child.report);
138
+ const status = ingestReportOutcome(child.report);
142
139
  io.stdout.write(`- target=${child.targetConnectionId} database=${child.metabaseDatabaseId} status=${status} job=${child.jobId} report=${child.report.id}\n`);
143
140
  }
144
141
  }
@@ -425,7 +422,7 @@ function initialRunMemoryFlowInput(args, runId) {
425
422
  };
426
423
  }
427
424
  function finalRunMemoryFlowInput(snapshot, report) {
428
- const status = reportStatus(report);
425
+ const status = ingestReportOutcome(report) === 'error' ? 'error' : 'done';
429
426
  return {
430
427
  ...snapshot,
431
428
  runId: report.runId,
@@ -574,7 +571,7 @@ export async function runKtxIngest(args, io = process, deps = {}) {
574
571
  finally {
575
572
  plainProgress?.flush();
576
573
  }
577
- return result.status === 'all_succeeded' ? 0 : 1;
574
+ return result.status === 'all_failed' ? 1 : 0;
578
575
  }
579
576
  const jobId = deps.jobIdFactory?.();
580
577
  let liveTui = null;
@@ -636,7 +633,7 @@ export async function runKtxIngest(args, io = process, deps = {}) {
636
633
  liveTui?.close();
637
634
  liveTui = null;
638
635
  io.stdout.write(formatMemoryFlowFinalSummary(latestMemoryFlowSnapshot));
639
- return reportStatus(result.report) === 'done' ? 0 : 1;
636
+ return ingestReportOutcome(result.report) === 'error' ? 1 : 0;
640
637
  }
641
638
  plainProgress?.flush();
642
639
  await writeReportRecord(result.report, runOutputMode, io, {
@@ -644,7 +641,7 @@ export async function runKtxIngest(args, io = process, deps = {}) {
644
641
  renderStoredMemoryFlow: deps.renderStoredMemoryFlow,
645
642
  env,
646
643
  });
647
- return reportStatus(result.report) === 'done' ? 0 : 1;
644
+ return ingestReportOutcome(result.report) === 'error' ? 1 : 0;
648
645
  }
649
646
  finally {
650
647
  plainProgress?.flush();
@@ -0,0 +1,11 @@
1
+ import type { KtxCliIo } from '../cli-runtime.js';
2
+ export interface BufferedCommandIo extends KtxCliIo {
3
+ stdoutText(): string;
4
+ stderrText(): string;
5
+ }
6
+ /**
7
+ * Captures stdout/stderr from a command (e.g. `runKtxConnection`) into buffers
8
+ * instead of the terminal. Callers decide whether to flush the captured text to
9
+ * the user or discard it.
10
+ */
11
+ export declare function createBufferedCommandIo(): BufferedCommandIo;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Captures stdout/stderr from a command (e.g. `runKtxConnection`) into buffers
3
+ * instead of the terminal. Callers decide whether to flush the captured text to
4
+ * the user or discard it.
5
+ */
6
+ export function createBufferedCommandIo() {
7
+ let stdout = '';
8
+ let stderr = '';
9
+ return {
10
+ stdout: {
11
+ isTTY: false,
12
+ write(chunk) {
13
+ stdout += chunk;
14
+ },
15
+ },
16
+ stderr: {
17
+ write(chunk) {
18
+ stderr += chunk;
19
+ },
20
+ },
21
+ stdoutText() {
22
+ return stdout;
23
+ },
24
+ stderrText() {
25
+ return stderr;
26
+ },
27
+ };
28
+ }
@@ -18,6 +18,11 @@ export type KtxKnowledgeArgs = {
18
18
  limit?: number;
19
19
  debug?: boolean;
20
20
  cliVersion: string;
21
+ } | {
22
+ command: 'read';
23
+ projectDir: string;
24
+ key: string;
25
+ userId: string;
21
26
  };
22
27
  type KtxKnowledgeIo = import('./cli-runtime.js').KtxCliIo;
23
28
  interface KtxKnowledgeDeps {
package/dist/knowledge.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { KtxIngestEmbeddingPortAdapter } from './context/llm/embedding-port.js';
2
2
  import { loadKtxProject } from './context/project/project.js';
3
- import { listLocalKnowledgePages, searchLocalKnowledgePages as defaultSearchLocalKnowledgePages } from './context/wiki/local-knowledge.js';
3
+ import { listLocalKnowledgePages, readLocalKnowledgePage, searchLocalKnowledgePages as defaultSearchLocalKnowledgePages, } from './context/wiki/local-knowledge.js';
4
4
  import { resolveProjectEmbeddingProvider, } from './embedding-resolution.js';
5
5
  import { resolveOutputMode } from './io/mode.js';
6
6
  import { createRankBadgeFormatter, printList } from './io/print-list.js';
@@ -72,6 +72,15 @@ export async function runKtxKnowledge(args, io = process, deps = {}) {
72
72
  });
73
73
  return 0;
74
74
  }
75
+ if (args.command === 'read') {
76
+ const page = await readLocalKnowledgePage(project, { key: args.key, userId: args.userId });
77
+ if (!page) {
78
+ throw new Error(`No wiki page found for key '${args.key}'`);
79
+ }
80
+ const raw = await project.fileStore.readFile(page.path);
81
+ io.stdout.write(raw.content);
82
+ return 0;
83
+ }
75
84
  if (args.command === 'search') {
76
85
  const embeddingService = await wikiSearchEmbeddingService(project, deps, { cliVersion: args.cliVersion }, io);
77
86
  const search = deps.searchLocalKnowledgePages ?? defaultSearchLocalKnowledgePages;
@@ -1,7 +1,7 @@
1
1
  import type { LanguageModel, TelemetrySettings, ToolCallRepairFunction, ToolSet } from 'ai';
2
2
  export declare const KTX_MODEL_ROLES: readonly ["default", "triage", "candidateExtraction", "curator", "reconcile", "repair"];
3
3
  export type KtxModelRole = (typeof KTX_MODEL_ROLES)[number];
4
- type KtxLlmBackend = 'anthropic' | 'vertex' | 'gateway' | 'claude-code';
4
+ type KtxLlmBackend = 'anthropic' | 'vertex' | 'gateway' | 'claude-code' | 'codex';
5
5
  export type KtxPromptCacheTtl = '5m' | '1h';
6
6
  type KtxJsonValue = null | string | number | boolean | KtxJsonValue[] | {
7
7
  [key: string]: KtxJsonValue | undefined;
@@ -1,14 +1,22 @@
1
1
  import { type DefaultLocalIngestAdaptersOptions } from './context/ingest/local-adapters.js';
2
+ import type { HistoricSqlDialect, HistoricSqlReader } from './context/ingest/adapters/historic-sql/types.js';
2
3
  import type { SourceAdapter } from './context/ingest/types.js';
3
4
  import type { KtxLocalProject } from './context/project/project.js';
4
5
  import type { SqlAnalysisPort } from './context/sql-analysis/ports.js';
5
- import { type ManagedPythonCoreDaemonOptions } from './managed-python-http.js';
6
+ import { type ManagedPythonDaemonHttpOptions } from './managed-python-http.js';
6
7
  import type { KtxOperationalLogger } from './io/logger.js';
7
8
  export interface KtxCliLocalIngestAdaptersOptions extends DefaultLocalIngestAdaptersOptions {
8
9
  historicSqlConnectionId?: string;
9
10
  sqlAnalysis?: SqlAnalysisPort;
10
11
  sqlAnalysisUrl?: string;
11
- managedDaemon?: ManagedPythonCoreDaemonOptions;
12
+ managedDaemon?: ManagedPythonDaemonHttpOptions;
12
13
  logger?: KtxOperationalLogger;
13
14
  }
15
+ export interface KtxCliHistoricSqlRuntime {
16
+ dialect: HistoricSqlDialect;
17
+ sqlAnalysis: SqlAnalysisPort;
18
+ reader: HistoricSqlReader;
19
+ queryClient: unknown;
20
+ }
21
+ export declare function createKtxCliHistoricSqlRuntime(project: KtxLocalProject, connectionId: string, options?: KtxCliLocalIngestAdaptersOptions): KtxCliHistoricSqlRuntime | undefined;
14
22
  export declare function createKtxCliLocalIngestAdapters(project: KtxLocalProject, options?: KtxCliLocalIngestAdaptersOptions): SourceAdapter[];
@@ -12,7 +12,7 @@ import { isKtxSqliteConnectionConfig } from './connectors/sqlite/connector.js';
12
12
  import { createSqlServerLiveDatabaseIntrospection } from './connectors/sqlserver/live-database-introspection.js';
13
13
  import { isKtxSqlServerConnectionConfig } from './connectors/sqlserver/connector.js';
14
14
  import { BigQueryHistoricSqlQueryHistoryReader } from './context/ingest/adapters/historic-sql/bigquery-query-history-reader.js';
15
- import { queryHistoryDialectForConnection } from './context/ingest/adapters/historic-sql/connection-dialect.js';
15
+ import { historicSqlDialectForConnectionDriver } from './context/ingest/adapters/historic-sql/connection-dialect.js';
16
16
  import { createDaemonLiveDatabaseIntrospection } from './context/ingest/adapters/live-database/daemon-introspection.js';
17
17
  import { createDefaultLocalIngestAdapters } from './context/ingest/local-adapters.js';
18
18
  import { LiveDatabaseSourceAdapter } from './context/ingest/adapters/live-database/live-database.adapter.js';
@@ -224,7 +224,12 @@ function historicSqlOptionsForLocalRun(project, options) {
224
224
  return undefined;
225
225
  }
226
226
  const connection = project.config.connections[connectionId];
227
- const dialect = queryHistoryDialectForConnection(connection);
227
+ // historicSqlConnectionId is only set when query history was explicitly
228
+ // requested for this run (e.g. `--query-history`), so resolve the dialect from
229
+ // driver capability rather than the persisted context.queryHistory.enabled
230
+ // flag — otherwise the adapter is missing and findAdapter('historic-sql')
231
+ // throws even though the run asked for it.
232
+ const dialect = historicSqlDialectForConnectionDriver(connection);
228
233
  if (!dialect) {
229
234
  return undefined;
230
235
  }
@@ -234,6 +239,7 @@ function historicSqlOptionsForLocalRun(project, options) {
234
239
  if (dialect === 'postgres') {
235
240
  return {
236
241
  ...base,
242
+ dialect,
237
243
  reader: new PostgresPgssReader(),
238
244
  queryClient: createEphemeralPostgresHistoricSqlClient(project, connectionId),
239
245
  };
@@ -245,6 +251,7 @@ function historicSqlOptionsForLocalRun(project, options) {
245
251
  }
246
252
  return {
247
253
  ...base,
254
+ dialect,
248
255
  reader: new BigQueryHistoricSqlQueryHistoryReader({
249
256
  projectId: bigQueryProjectId(connection, process.env),
250
257
  region: bigQueryRegion(connection),
@@ -254,6 +261,7 @@ function historicSqlOptionsForLocalRun(project, options) {
254
261
  }
255
262
  return {
256
263
  ...base,
264
+ dialect,
257
265
  reader: new SnowflakeHistoricSqlQueryHistoryReader(),
258
266
  queryClient: {
259
267
  async executeQuery(query) {
@@ -264,8 +272,16 @@ function historicSqlOptionsForLocalRun(project, options) {
264
272
  },
265
273
  };
266
274
  }
275
+ export function createKtxCliHistoricSqlRuntime(project, connectionId, options = {}) {
276
+ return historicSqlOptionsForLocalRun(project, {
277
+ ...options,
278
+ historicSqlConnectionId: connectionId,
279
+ });
280
+ }
267
281
  export function createKtxCliLocalIngestAdapters(project, options = {}) {
268
- const historicSql = historicSqlOptionsForLocalRun(project, options);
282
+ const historicSql = options.historicSqlConnectionId
283
+ ? createKtxCliHistoricSqlRuntime(project, options.historicSqlConnectionId, options)
284
+ : undefined;
269
285
  const base = createDefaultLocalIngestAdapters(project, {
270
286
  ...options,
271
287
  databaseIntrospection: ktxCliDaemonDatabaseIntrospectionOptions(options),
@@ -53,8 +53,7 @@ export function formatSetupNextStepLines(state, indent = ' ') {
53
53
  }
54
54
  if (!state.contextReady) {
55
55
  return [
56
- `${indent}Build KTX context next.`,
57
- `${indent}Run ingest to build database schema context before context-source ingest.`,
56
+ `${indent}Setup is complete. The only step left is to build context for your agents.`,
58
57
  ...commandLines(KTX_CONTEXT_BUILD_COMMANDS, indent),
59
58
  ];
60
59
  }
@@ -0,0 +1,6 @@
1
+ import type { KtxProgressPort } from './context/scan/types.js';
2
+ import type { KtxIngestProgressUpdate } from './ingest.js';
3
+ export interface AggregateProgressState {
4
+ progress: number;
5
+ }
6
+ export declare function createAggregateProgressPort(onProgress: (update: KtxIngestProgressUpdate) => void, state?: AggregateProgressState, start?: number, weight?: number): KtxProgressPort;
@@ -0,0 +1,18 @@
1
+ export function createAggregateProgressPort(onProgress, state = { progress: 0 }, start = 0, weight = 1) {
2
+ return {
3
+ async update(value, message, options) {
4
+ const absoluteValue = start + Math.max(0, Math.min(1, value)) * weight;
5
+ state.progress = Math.max(state.progress, Math.min(1, absoluteValue));
6
+ if (!message)
7
+ return;
8
+ onProgress({
9
+ percent: Math.max(0, Math.min(100, Math.round(state.progress * 100))),
10
+ message,
11
+ ...(options?.transient !== undefined ? { transient: options.transient } : {}),
12
+ });
13
+ },
14
+ startPhase(phaseWeight) {
15
+ return createAggregateProgressPort(onProgress, state, state.progress, weight * phaseWeight);
16
+ },
17
+ };
18
+ }
@@ -11,7 +11,7 @@ const DATABASE_INGEST_REPLACEMENTS = [
11
11
  'Database enrichment failed after schema context completed',
12
12
  ],
13
13
  [/\bstructural scan\b/gi, 'schema context'],
14
- [/\benriched scan\b/gi, 'deep database ingest'],
14
+ [/\benriched scan\b/gi, 'database ingest'],
15
15
  [/\bscan results\b/gi, 'database context'],
16
16
  ];
17
17
  export function publicDatabaseIngestMessage(message) {