@kaelio/ktx 0.11.0 → 0.13.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 (212) hide show
  1. package/assets/python/kaelio_ktx-0.13.0-py3-none-any.whl +0 -0
  2. package/assets/python/manifest.json +4 -4
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/admin.js +1 -1
  5. package/dist/clack.d.ts +16 -0
  6. package/dist/clack.js +37 -6
  7. package/dist/claude-code-prompt-caching.js +1 -1
  8. package/dist/cli-program.js +3 -3
  9. package/dist/cli-runtime.js +2 -2
  10. package/dist/commands/connection-commands.js +1 -1
  11. package/dist/commands/ingest-commands.js +4 -4
  12. package/dist/commands/mcp-commands.js +12 -12
  13. package/dist/commands/runtime-commands.js +4 -4
  14. package/dist/commands/setup-commands.js +19 -5
  15. package/dist/commands/sl-commands.js +1 -1
  16. package/dist/commands/sql-commands.js +1 -1
  17. package/dist/commands/status-commands.js +1 -1
  18. package/dist/connection.js +15 -3
  19. package/dist/connectors/bigquery/connector.js +1 -14
  20. package/dist/connectors/clickhouse/connector.js +2 -16
  21. package/dist/connectors/duckdb/federated-attach.d.ts +7 -0
  22. package/dist/connectors/duckdb/federated-attach.js +86 -0
  23. package/dist/connectors/duckdb/federated-executor.d.ts +5 -0
  24. package/dist/connectors/duckdb/federated-executor.js +59 -0
  25. package/dist/connectors/mysql/connector.js +2 -16
  26. package/dist/connectors/postgres/connector.js +1 -14
  27. package/dist/connectors/shared/string-reference.d.ts +6 -0
  28. package/dist/connectors/shared/string-reference.js +19 -0
  29. package/dist/connectors/snowflake/connector.d.ts +1 -1
  30. package/dist/connectors/snowflake/connector.js +1 -14
  31. package/dist/connectors/sqlite/connector.js +2 -25
  32. package/dist/connectors/sqlserver/connector.js +4 -17
  33. package/dist/context/connections/connection-type.d.ts +1 -1
  34. package/dist/context/connections/federation.d.ts +33 -0
  35. package/dist/context/connections/federation.js +51 -0
  36. package/dist/context/connections/local-warehouse-descriptor.d.ts +2 -0
  37. package/dist/context/connections/project-sql-executor.d.ts +18 -0
  38. package/dist/context/connections/project-sql-executor.js +39 -0
  39. package/dist/context/connections/query-executor.d.ts +2 -2
  40. package/dist/context/connections/read-only-sql.d.ts +1 -0
  41. package/dist/context/connections/read-only-sql.js +119 -4
  42. package/dist/context/connections/resolve-connection.d.ts +12 -0
  43. package/dist/context/connections/resolve-connection.js +37 -0
  44. package/dist/context/core/git-env.d.ts +4 -0
  45. package/dist/context/core/git-env.js +5 -1
  46. package/dist/context/core/git.service.d.ts +23 -0
  47. package/dist/context/core/git.service.js +71 -8
  48. package/dist/context/ingest/adapters/historic-sql/projection.js +2 -1
  49. package/dist/context/ingest/adapters/live-database/manifest.d.ts +3 -0
  50. package/dist/context/ingest/adapters/live-database/manifest.js +19 -11
  51. package/dist/context/ingest/adapters/looker/client.js +7 -2
  52. package/dist/context/ingest/adapters/looker/factory.d.ts +8 -1
  53. package/dist/context/ingest/adapters/looker/factory.js +9 -0
  54. package/dist/context/ingest/adapters/looker/mapping.js +1 -1
  55. package/dist/context/ingest/adapters/looker/types.d.ts +1 -1
  56. package/dist/context/ingest/adapters/metabase/client.d.ts +1 -1
  57. package/dist/context/ingest/adapters/metabase/client.js +1 -1
  58. package/dist/context/ingest/adapters/metabase/local-metabase.adapter.js +1 -1
  59. package/dist/context/ingest/adapters/metabase/mapping.js +6 -6
  60. package/dist/context/ingest/artifact-gates.d.ts +2 -6
  61. package/dist/context/ingest/artifact-gates.js +5 -47
  62. package/dist/context/ingest/constrained-repair.d.ts +55 -0
  63. package/dist/context/ingest/constrained-repair.js +167 -0
  64. package/dist/context/ingest/final-gate-repair.d.ts +9 -11
  65. package/dist/context/ingest/final-gate-repair.js +40 -128
  66. package/dist/context/ingest/finalization-scope.d.ts +1 -1
  67. package/dist/context/ingest/finalization-scope.js +15 -15
  68. package/dist/context/ingest/ingest-bundle.runner.d.ts +1 -0
  69. package/dist/context/ingest/ingest-bundle.runner.js +101 -67
  70. package/dist/context/ingest/isolated-diff/patch-integrator.d.ts +6 -13
  71. package/dist/context/ingest/isolated-diff/patch-integrator.js +32 -109
  72. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +8 -9
  73. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +63 -141
  74. package/dist/context/ingest/local-bundle-runtime.d.ts +2 -0
  75. package/dist/context/ingest/local-bundle-runtime.js +9 -10
  76. package/dist/context/ingest/local-ingest.d.ts +2 -0
  77. package/dist/context/ingest/local-ingest.js +2 -0
  78. package/dist/context/ingest/memory-flow/view-model.js +1 -1
  79. package/dist/context/ingest/stages/stage-3-work-units.d.ts +2 -6
  80. package/dist/context/ingest/stages/stage-3-work-units.js +2 -1
  81. package/dist/context/ingest/stages/validate-wu-sources.d.ts +7 -1
  82. package/dist/context/ingest/stages/validate-wu-sources.js +109 -4
  83. package/dist/context/ingest/tools/warehouse-verification/create-warehouse-verification-tools.d.ts +2 -0
  84. package/dist/context/ingest/tools/warehouse-verification/create-warehouse-verification-tools.js +1 -1
  85. package/dist/context/ingest/tools/warehouse-verification/discover-data.tool.js +3 -3
  86. package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.d.ts +3 -1
  87. package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.js +15 -1
  88. package/dist/context/llm/ai-sdk-runtime.js +2 -2
  89. package/dist/context/llm/claude-code-runtime.js +19 -3
  90. package/dist/context/llm/local-config.js +1 -1
  91. package/dist/context/llm/runtime-tools.js +2 -2
  92. package/dist/context/mcp/context-tools.js +33 -8
  93. package/dist/context/mcp/local-project-ports.js +63 -89
  94. package/dist/context/mcp/types.d.ts +2 -0
  95. package/dist/context/memory/local-memory.js +4 -1
  96. package/dist/context/memory/memory-agent.service.js +1 -1
  97. package/dist/context/project/config.d.ts +11 -4
  98. package/dist/context/project/config.js +85 -30
  99. package/dist/context/project/driver-schemas.js +1 -1
  100. package/dist/context/project/mappings-yaml-schema.js +2 -2
  101. package/dist/context/project/project.js +12 -4
  102. package/dist/context/scan/description-generation.js +4 -4
  103. package/dist/context/scan/local-enrichment-artifacts.js +33 -4
  104. package/dist/context/scan/local-scan.js +2 -2
  105. package/dist/context/scan/local-structural-artifacts.js +5 -5
  106. package/dist/context/scan/relationship-benchmark-report.js +1 -1
  107. package/dist/context/scan/relationship-discovery.js +3 -3
  108. package/dist/context/scan/relationship-llm-proposal.js +3 -3
  109. package/dist/context/sl/local-query.js +31 -44
  110. package/dist/context/sl/local-sl.d.ts +0 -8
  111. package/dist/context/sl/local-sl.js +71 -70
  112. package/dist/context/sl/semantic-layer.service.d.ts +25 -8
  113. package/dist/context/sl/semantic-layer.service.js +109 -56
  114. package/dist/context/sl/source-files.d.ts +48 -0
  115. package/dist/context/sl/source-files.js +138 -0
  116. package/dist/context/sl/tools/base-semantic-layer.tool.d.ts +2 -2
  117. package/dist/context/sl/tools/base-semantic-layer.tool.js +2 -7
  118. package/dist/context/sl/tools/sl-edit-source.tool.js +10 -8
  119. package/dist/context/sl/tools/sl-warehouse-validation.js +55 -27
  120. package/dist/context/sl/tools/sl-write-source.tool.js +12 -9
  121. package/dist/context/sql-analysis/dialect.d.ts +2 -0
  122. package/dist/context/sql-analysis/dialect.js +20 -0
  123. package/dist/context/tools/base-tool.d.ts +6 -19
  124. package/dist/context/tools/base-tool.js +0 -14
  125. package/dist/context-build-view.js +5 -5
  126. package/dist/database-tree-picker.js +18 -3
  127. package/dist/demo-assets.js +0 -1
  128. package/dist/doctor.d.ts +1 -1
  129. package/dist/doctor.js +31 -23
  130. package/dist/errors.d.ts +31 -0
  131. package/dist/errors.js +44 -0
  132. package/dist/ingest-query-executor.d.ts +2 -0
  133. package/dist/ingest-query-executor.js +8 -22
  134. package/dist/ingest.d.ts +1 -1
  135. package/dist/ingest.js +8 -2
  136. package/dist/io/symbols.d.ts +2 -0
  137. package/dist/io/symbols.js +2 -0
  138. package/dist/io/tty.d.ts +8 -0
  139. package/dist/io/tty.js +16 -0
  140. package/dist/llm/embedding-health.js +1 -1
  141. package/dist/llm/embedding-provider.js +3 -3
  142. package/dist/llm/model-provider.js +1 -1
  143. package/dist/local-adapters.d.ts +1 -0
  144. package/dist/local-adapters.js +2 -2
  145. package/dist/local-scan-connectors.js +1 -1
  146. package/dist/managed-local-embeddings.js +17 -8
  147. package/dist/managed-mcp-daemon.js +3 -3
  148. package/dist/managed-python-command.d.ts +7 -0
  149. package/dist/managed-python-command.js +34 -8
  150. package/dist/managed-python-daemon.js +2 -2
  151. package/dist/managed-python-http.js +3 -3
  152. package/dist/managed-python-runtime.d.ts +30 -1
  153. package/dist/managed-python-runtime.js +134 -18
  154. package/dist/managed-uv-release.d.ts +7 -0
  155. package/dist/managed-uv-release.js +11 -0
  156. package/dist/mcp-http-server.js +4 -4
  157. package/dist/mcp-server-factory.js +3 -3
  158. package/dist/mcp-stdio-server.js +1 -1
  159. package/dist/memory-flow-hud.js +2 -2
  160. package/dist/next-steps.js +2 -2
  161. package/dist/prompt-navigation.d.ts +17 -0
  162. package/dist/prompt-navigation.js +49 -3
  163. package/dist/prompts/memory_agent_bundle_ingest_work_unit.md +2 -2
  164. package/dist/prompts/memory_agent_external_ingest.md +2 -2
  165. package/dist/public-ingest-copy.js +1 -1
  166. package/dist/public-ingest.js +3 -3
  167. package/dist/release-version.js +1 -1
  168. package/dist/runtime-requirements.js +1 -1
  169. package/dist/runtime.js +9 -9
  170. package/dist/scan.js +1 -1
  171. package/dist/setup-agents.d.ts +21 -15
  172. package/dist/setup-agents.js +143 -66
  173. package/dist/setup-banner.d.ts +20 -0
  174. package/dist/setup-banner.js +39 -0
  175. package/dist/setup-context.js +24 -15
  176. package/dist/setup-databases.d.ts +3 -0
  177. package/dist/setup-databases.js +47 -59
  178. package/dist/setup-demo-tour.js +12 -8
  179. package/dist/setup-embeddings.js +9 -9
  180. package/dist/setup-interrupt.js +1 -1
  181. package/dist/setup-models.d.ts +4 -1
  182. package/dist/setup-models.js +54 -28
  183. package/dist/setup-project.js +29 -5
  184. package/dist/setup-prompts.js +16 -1
  185. package/dist/setup-ready-menu.js +1 -1
  186. package/dist/setup-sources.js +28 -12
  187. package/dist/setup.d.ts +1 -0
  188. package/dist/setup.js +14 -13
  189. package/dist/skills/analytics/SKILL.md +3 -3
  190. package/dist/skills/dbt_ingest/SKILL.md +3 -3
  191. package/dist/skills/looker_ingest/SKILL.md +3 -3
  192. package/dist/skills/lookml_ingest/SKILL.md +7 -7
  193. package/dist/skills/metabase_ingest/SKILL.md +4 -4
  194. package/dist/skills/metricflow_ingest/SKILL.md +15 -15
  195. package/dist/skills/notion_synthesize/SKILL.md +1 -1
  196. package/dist/skills/sl/SKILL.md +3 -3
  197. package/dist/skills/sl_capture/SKILL.md +1 -1
  198. package/dist/skills/wiki_capture/SKILL.md +1 -1
  199. package/dist/source-mapping.js +1 -1
  200. package/dist/sql.d.ts +2 -0
  201. package/dist/sql.js +35 -53
  202. package/dist/startup-profile.js +1 -1
  203. package/dist/status-project.d.ts +0 -2
  204. package/dist/status-project.js +4 -6
  205. package/dist/telemetry/events.d.ts +3 -2
  206. package/dist/telemetry/events.js +11 -1
  207. package/dist/telemetry/exception.js +14 -0
  208. package/dist/text-ingest.js +1 -1
  209. package/dist/tree-picker-tui.d.ts +0 -1
  210. package/dist/tree-picker-tui.js +2 -3
  211. package/package.json +2 -1
  212. package/assets/python/kaelio_ktx-0.11.0-py3-none-any.whl +0 -0
@@ -0,0 +1,167 @@
1
+ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
2
+ import { dirname, join } from 'node:path';
3
+ import { z } from 'zod';
4
+ import { traceTimed } from './ingest-trace.js';
5
+ const readRepairFileSchema = z.object({
6
+ path: z.string().min(1),
7
+ });
8
+ const writeRepairFileSchema = z.object({
9
+ path: z.string().min(1),
10
+ content: z.string(),
11
+ });
12
+ function normalizeRepoPath(path) {
13
+ const normalized = path.replace(/\\/g, '/').replace(/^\/+/, '');
14
+ const parts = normalized.split('/').filter((part) => part.length > 0);
15
+ if (parts.length === 0 || parts.some((part) => part === '.' || part === '..')) {
16
+ throw new Error(`repair path must be a repository-relative path: ${path}`);
17
+ }
18
+ return parts.join('/');
19
+ }
20
+ function assertAllowedPath(path, allowedPaths) {
21
+ const normalized = normalizeRepoPath(path);
22
+ if (!allowedPaths.has(normalized)) {
23
+ throw new Error(`repair path not allowed: ${normalized}`);
24
+ }
25
+ return normalized;
26
+ }
27
+ async function readOptionalFile(path) {
28
+ try {
29
+ return { exists: true, content: await readFile(path, 'utf-8') };
30
+ }
31
+ catch (error) {
32
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
33
+ return { exists: false, content: '' };
34
+ }
35
+ throw error;
36
+ }
37
+ }
38
+ function buildRepairFileTools(context) {
39
+ return {
40
+ read_repair_file: {
41
+ name: 'read_repair_file',
42
+ description: 'Read one allowed file from the integration worktree.',
43
+ inputSchema: readRepairFileSchema,
44
+ execute: async ({ path }) => {
45
+ const normalized = assertAllowedPath(path, context.allowedPaths);
46
+ const file = await readOptionalFile(join(context.workdir, normalized));
47
+ return {
48
+ markdown: file.exists ? file.content : `(missing file: ${normalized})`,
49
+ structured: { path: normalized, exists: file.exists },
50
+ };
51
+ },
52
+ },
53
+ write_repair_file: {
54
+ name: 'write_repair_file',
55
+ description: 'Replace one allowed integration worktree file with repaired text content.',
56
+ inputSchema: writeRepairFileSchema,
57
+ execute: async ({ path, content }) => {
58
+ const normalized = assertAllowedPath(path, context.allowedPaths);
59
+ const fullPath = join(context.workdir, normalized);
60
+ await mkdir(dirname(fullPath), { recursive: true });
61
+ await writeFile(fullPath, content, 'utf-8');
62
+ context.editedPaths.add(normalized);
63
+ return {
64
+ markdown: `Wrote ${normalized}`,
65
+ structured: { path: normalized, bytes: Buffer.byteLength(content) },
66
+ };
67
+ },
68
+ },
69
+ };
70
+ }
71
+ export function buildDeleteRepairFileTool(context) {
72
+ const deleteRepairFileSchema = z.object({
73
+ path: z.string().min(1),
74
+ });
75
+ return {
76
+ delete_repair_file: {
77
+ name: 'delete_repair_file',
78
+ description: 'Delete one allowed integration worktree file when the failed patch proves the deletion is correct.',
79
+ inputSchema: deleteRepairFileSchema,
80
+ execute: async ({ path }) => {
81
+ const normalized = assertAllowedPath(path, context.allowedPaths);
82
+ await rm(join(context.workdir, normalized), { force: true });
83
+ context.editedPaths.add(normalized);
84
+ return {
85
+ markdown: `Deleted ${normalized}`,
86
+ structured: { path: normalized },
87
+ };
88
+ },
89
+ },
90
+ };
91
+ }
92
+ export async function runConstrainedRepairLoop(input) {
93
+ const allowedPaths = new Set(input.allowedPaths.map(normalizeRepoPath));
94
+ const sortedAllowedPaths = [...allowedPaths].sort();
95
+ const maxAttempts = input.maxAttempts ?? 2;
96
+ const stepBudget = input.stepBudget ?? 16;
97
+ // Edits persist in the worktree across attempts, so the verified set and the
98
+ // reported changedPaths accumulate over the whole loop.
99
+ const editedPaths = new Set();
100
+ let lastFailure = 'repair did not run';
101
+ let previousFailure = null;
102
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
103
+ let noChangeDeclaration = null;
104
+ const toolContext = {
105
+ workdir: input.workdir,
106
+ allowedPaths,
107
+ editedPaths,
108
+ declareNoChange: (reason) => {
109
+ noChangeDeclaration = reason;
110
+ },
111
+ };
112
+ const traceData = {
113
+ ...input.traceData,
114
+ attempt,
115
+ maxAttempts,
116
+ allowedPaths: sortedAllowedPaths,
117
+ };
118
+ const result = await traceTimed(input.trace, input.tracePhase, input.traceEventName, traceData, async () => input.agentRunner.runLoop({
119
+ modelRole: 'repair',
120
+ systemPrompt: input.systemPrompt,
121
+ userPrompt: input.buildUserPrompt({ attempt, maxAttempts, previousFailure }),
122
+ toolSet: {
123
+ ...buildRepairFileTools(toolContext),
124
+ ...(input.buildExtraTools?.(toolContext) ?? {}),
125
+ },
126
+ stepBudget,
127
+ telemetryTags: input.telemetryTags,
128
+ abortSignal: input.abortSignal,
129
+ }));
130
+ if (result.stopReason === 'error') {
131
+ lastFailure = result.error?.message ?? 'repair agent loop errored';
132
+ previousFailure = lastFailure;
133
+ await input.trace.event('error', input.tracePhase, `${input.traceEventName}_failed`, traceData, result.error);
134
+ continue;
135
+ }
136
+ const changedPaths = [...editedPaths].sort();
137
+ if (changedPaths.length === 0 && noChangeDeclaration === null) {
138
+ // Nothing changed and nothing was claimed: the failed check would fail
139
+ // identically, so skip verification and retry.
140
+ lastFailure = input.noChangeFailureReason;
141
+ previousFailure = lastFailure;
142
+ await input.trace.event('error', input.tracePhase, `${input.traceEventName}_failed`, {
143
+ ...traceData,
144
+ reason: lastFailure,
145
+ });
146
+ continue;
147
+ }
148
+ const verification = await input.verify(changedPaths);
149
+ if (!verification.ok) {
150
+ lastFailure = verification.reason;
151
+ previousFailure = lastFailure;
152
+ await input.trace.event('error', input.tracePhase, `${input.traceEventName}_failed`, {
153
+ ...traceData,
154
+ changedPaths,
155
+ reason: lastFailure,
156
+ });
157
+ continue;
158
+ }
159
+ await input.trace.event('debug', input.tracePhase, `${input.traceEventName}_repaired`, {
160
+ ...traceData,
161
+ changedPaths,
162
+ ...(noChangeDeclaration !== null ? { noChangeDeclaration } : {}),
163
+ });
164
+ return { status: 'repaired', attempts: attempt, changedPaths };
165
+ }
166
+ return { status: 'failed', attempts: maxAttempts, reason: lastFailure };
167
+ }
@@ -1,16 +1,8 @@
1
1
  import type { AgentRunnerPort } from '../../context/llm/runtime-port.js';
2
- import type { TouchedSlSource } from '../../context/tools/touched-sl-sources.js';
2
+ import type { ConstrainedRepairResult, RepairVerification } from './constrained-repair.js';
3
3
  import type { IngestTraceWriter } from './ingest-trace.js';
4
4
  type FinalGateRepairKind = 'patch_semantic_gate' | 'final_artifact_gate';
5
- export type FinalGateRepairResult = {
6
- status: 'repaired';
7
- attempts: number;
8
- changedPaths: string[];
9
- } | {
10
- status: 'failed';
11
- attempts: number;
12
- reason: string;
13
- };
5
+ export type FinalGateRepairResult = ConstrainedRepairResult;
14
6
  export interface RepairFinalGateFailureInput {
15
7
  agentRunner: AgentRunnerPort;
16
8
  workdir: string;
@@ -18,13 +10,19 @@ export interface RepairFinalGateFailureInput {
18
10
  allowedPaths: string[];
19
11
  trace: IngestTraceWriter;
20
12
  repairKind: FinalGateRepairKind;
13
+ /**
14
+ * Re-runs the failed gate against the current worktree. The repair counts
15
+ * as successful only when this passes — editing files is not the success
16
+ * signal.
17
+ */
18
+ verify(changedPaths: string[]): Promise<RepairVerification>;
21
19
  maxAttempts?: number;
22
20
  stepBudget?: number;
23
21
  abortSignal?: AbortSignal;
24
22
  }
25
23
  export declare function finalGateRepairPaths(input: {
26
24
  changedWikiPageKeys: string[];
27
- touchedSlSources: TouchedSlSource[];
25
+ touchedSlSourcePaths: string[];
28
26
  }): string[];
29
27
  export declare function repairFinalGateFailure(input: RepairFinalGateFailureInput): Promise<FinalGateRepairResult>;
30
28
  export {};
@@ -1,43 +1,8 @@
1
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
- import { dirname, join } from 'node:path';
3
1
  import { z } from 'zod';
4
- import { traceTimed } from './ingest-trace.js';
5
- const readRepairFileSchema = z.object({
6
- path: z.string().min(1),
7
- });
8
- const writeRepairFileSchema = z.object({
9
- path: z.string().min(1),
10
- content: z.string(),
11
- });
12
- function normalizeRepoPath(path) {
13
- const normalized = path.replace(/\\/g, '/').replace(/^\/+/, '');
14
- const parts = normalized.split('/').filter((part) => part.length > 0);
15
- if (parts.length === 0 || parts.some((part) => part === '.' || part === '..')) {
16
- throw new Error(`gate repair path must be a repository-relative path: ${path}`);
17
- }
18
- return parts.join('/');
19
- }
20
- function assertAllowedPath(path, allowedPaths) {
21
- const normalized = normalizeRepoPath(path);
22
- if (!allowedPaths.has(normalized)) {
23
- throw new Error(`gate repair path not allowed: ${normalized}`);
24
- }
25
- return normalized;
26
- }
27
- async function readOptionalFile(path) {
28
- try {
29
- return { exists: true, content: await readFile(path, 'utf-8') };
30
- }
31
- catch (error) {
32
- if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
33
- return { exists: false, content: '' };
34
- }
35
- throw error;
36
- }
37
- }
2
+ import { runConstrainedRepairLoop } from './constrained-repair.js';
38
3
  function buildGateRepairSystemPrompt() {
39
4
  return `<role>
40
- You repair one KTX isolated-diff artifact gate failure inside the integration worktree.
5
+ You repair one ktx isolated-diff artifact gate failure inside the integration worktree.
41
6
  </role>
42
7
 
43
8
  <rules>
@@ -51,6 +16,9 @@ You repair one KTX isolated-diff artifact gate failure inside the integration wo
51
16
  </rules>`;
52
17
  }
53
18
  function buildGateRepairUserPrompt(input) {
19
+ const previousFailureBlock = input.previousFailure
20
+ ? `\nPrevious attempt did not pass the gate:\n${input.previousFailure}\n`
21
+ : '';
54
22
  return `Repair isolated-diff artifact gates.
55
23
 
56
24
  Repair kind: ${input.repairKind}
@@ -61,119 +29,63 @@ ${input.allowedPaths.map((path) => `- ${path}`).join('\n')}
61
29
 
62
30
  Gate error:
63
31
  ${input.gateError}
64
-
32
+ ${previousFailureBlock}
65
33
  Use read_gate_error first. Then inspect only the allowed files, write the
66
34
  minimal repaired content, and stop.`;
67
35
  }
68
- function buildToolSet(input) {
36
+ function buildReadGateErrorTool(gateError) {
69
37
  return {
70
38
  read_gate_error: {
71
39
  name: 'read_gate_error',
72
40
  description: 'Read the artifact gate failure that must be repaired.',
73
41
  inputSchema: z.object({}),
74
42
  execute: async () => ({
75
- markdown: input.gateError,
76
- structured: { gateError: input.gateError },
43
+ markdown: gateError,
44
+ structured: { gateError },
77
45
  }),
78
46
  },
79
- read_repair_file: {
80
- name: 'read_repair_file',
81
- description: 'Read one allowed file from the integration worktree.',
82
- inputSchema: readRepairFileSchema,
83
- execute: async ({ path }) => {
84
- const normalized = assertAllowedPath(path, input.allowedPaths);
85
- const file = await readOptionalFile(join(input.workdir, normalized));
86
- return {
87
- markdown: file.exists ? file.content : `(missing file: ${normalized})`,
88
- structured: { path: normalized, exists: file.exists },
89
- };
90
- },
91
- },
92
- write_repair_file: {
93
- name: 'write_repair_file',
94
- description: 'Replace one allowed integration worktree file with repaired text content.',
95
- inputSchema: writeRepairFileSchema,
96
- execute: async ({ path, content }) => {
97
- const normalized = assertAllowedPath(path, input.allowedPaths);
98
- const fullPath = join(input.workdir, normalized);
99
- await mkdir(dirname(fullPath), { recursive: true });
100
- await writeFile(fullPath, content, 'utf-8');
101
- input.editedPaths.add(normalized);
102
- return {
103
- markdown: `Wrote ${normalized}`,
104
- structured: { path: normalized, bytes: Buffer.byteLength(content) },
105
- };
106
- },
107
- },
108
47
  };
109
48
  }
110
49
  export function finalGateRepairPaths(input) {
111
50
  return [
112
51
  ...new Set([
113
- ...input.touchedSlSources.map((source) => `semantic-layer/${source.connectionId}/${source.sourceName}.yaml`),
52
+ ...input.touchedSlSourcePaths,
114
53
  ...input.changedWikiPageKeys.map((pageKey) => `wiki/global/${pageKey}.md`),
115
54
  ]),
116
55
  ].sort();
117
56
  }
118
57
  export async function repairFinalGateFailure(input) {
119
- const allowedPaths = new Set(input.allowedPaths.map(normalizeRepoPath));
120
- const maxAttempts = input.maxAttempts ?? 1;
121
- const stepBudget = input.stepBudget ?? 16;
122
- let lastFailure = 'gate repair did not run';
123
- for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
124
- const editedPaths = new Set();
125
- const sortedAllowedPaths = [...allowedPaths].sort();
126
- const traceData = {
58
+ return runConstrainedRepairLoop({
59
+ agentRunner: input.agentRunner,
60
+ workdir: input.workdir,
61
+ allowedPaths: input.allowedPaths,
62
+ trace: input.trace,
63
+ tracePhase: 'gate_repair',
64
+ traceEventName: 'gate_repair',
65
+ traceData: {
66
+ repairKind: input.repairKind,
67
+ gateError: input.gateError,
68
+ },
69
+ systemPrompt: buildGateRepairSystemPrompt(),
70
+ buildUserPrompt: ({ attempt, maxAttempts, previousFailure }) => buildGateRepairUserPrompt({
71
+ gateError: input.gateError,
72
+ allowedPaths: [...input.allowedPaths].sort(),
127
73
  repairKind: input.repairKind,
128
74
  attempt,
129
75
  maxAttempts,
130
- allowedPaths: sortedAllowedPaths,
131
- gateError: input.gateError,
132
- };
133
- const result = await traceTimed(input.trace, 'gate_repair', 'gate_repair', traceData, async () => input.agentRunner.runLoop({
134
- modelRole: 'repair',
135
- systemPrompt: buildGateRepairSystemPrompt(),
136
- userPrompt: buildGateRepairUserPrompt({
137
- gateError: input.gateError,
138
- allowedPaths: sortedAllowedPaths,
139
- repairKind: input.repairKind,
140
- attempt,
141
- maxAttempts,
142
- }),
143
- toolSet: buildToolSet({
144
- workdir: input.workdir,
145
- gateError: input.gateError,
146
- allowedPaths,
147
- editedPaths,
148
- }),
149
- stepBudget,
150
- telemetryTags: {
151
- operationName: 'ingest-isolated-diff-gate-repair',
152
- source: input.trace.context.sourceKey,
153
- jobId: input.trace.context.jobId,
154
- repairKind: input.repairKind,
155
- },
156
- abortSignal: input.abortSignal,
157
- }));
158
- if (result.stopReason === 'error') {
159
- lastFailure = result.error?.message ?? 'gate repair agent loop errored';
160
- await input.trace.event('error', 'gate_repair', 'gate_repair_failed', traceData, result.error);
161
- continue;
162
- }
163
- const changedPaths = [...editedPaths].sort();
164
- if (changedPaths.length === 0) {
165
- lastFailure = 'gate repair completed without editing an allowed path';
166
- await input.trace.event('error', 'gate_repair', 'gate_repair_failed', {
167
- ...traceData,
168
- reason: lastFailure,
169
- });
170
- continue;
171
- }
172
- await input.trace.event('debug', 'gate_repair', 'gate_repair_repaired', {
173
- ...traceData,
174
- changedPaths,
175
- });
176
- return { status: 'repaired', attempts: attempt, changedPaths };
177
- }
178
- return { status: 'failed', attempts: maxAttempts, reason: lastFailure };
76
+ previousFailure,
77
+ }),
78
+ buildExtraTools: () => buildReadGateErrorTool(input.gateError),
79
+ verify: input.verify,
80
+ noChangeFailureReason: 'gate repair completed without editing an allowed path',
81
+ telemetryTags: {
82
+ operationName: 'ingest-isolated-diff-gate-repair',
83
+ source: input.trace.context.sourceKey,
84
+ jobId: input.trace.context.jobId,
85
+ repairKind: input.repairKind,
86
+ },
87
+ maxAttempts: input.maxAttempts,
88
+ stepBudget: input.stepBudget ?? 16,
89
+ abortSignal: input.abortSignal,
90
+ });
179
91
  }
@@ -17,6 +17,6 @@ interface CompareFinalizationDeclarationsInput {
17
17
  derivedChangedWikiPageKeys: string[];
18
18
  }
19
19
  export declare function deriveFinalizationWikiPageKeys(paths: string[]): string[];
20
- export declare function deriveFinalizationTouchedSources(input: DeriveTouchedSourcesInput): Promise<DeriveTouchedSourcesResult>;
20
+ export declare function deriveFinalizationTouchedSources(input: DeriveTouchedSourcesInput): DeriveTouchedSourcesResult;
21
21
  export declare function compareFinalizationDeclarations(input: CompareFinalizationDeclarationsInput): IngestReportFinalizationMismatch[];
22
22
  export {};
@@ -1,3 +1,4 @@
1
+ import { isSlYamlPath } from '../../context/sl/source-files.js';
1
2
  function uniqueSorted(values) {
2
3
  return [...new Set(values.filter((value) => value.length > 0))].sort();
3
4
  }
@@ -28,32 +29,31 @@ export function deriveFinalizationWikiPageKeys(paths) {
28
29
  .filter((path) => !path.slice('wiki/global/'.length, -'.md'.length).includes('/'))
29
30
  .map((path) => path.slice('wiki/global/'.length, -'.md'.length)));
30
31
  }
31
- export async function deriveFinalizationTouchedSources(input) {
32
+ // Source identity is the in-file `name:`; filenames are derived labels (see
33
+ // source-files.ts), so a changed path — manifest shard or standalone file —
34
+ // cannot be mapped to a source by parsing its filename. Instead, every changed
35
+ // semantic-layer file is attributed through the before/after diff of its
36
+ // connection's composed sources. A changed file whose connection diff is empty
37
+ // cannot be attributed to any source and is surfaced as unresolved.
38
+ export function deriveFinalizationTouchedSources(input) {
32
39
  const touched = new Map();
33
40
  const unresolvedPaths = [];
41
+ const pathsByConnection = new Map();
34
42
  for (const path of input.changedPaths) {
35
- if (!path.startsWith('semantic-layer/') || !(path.endsWith('.yaml') || path.endsWith('.yml'))) {
43
+ if (!path.startsWith('semantic-layer/') || !isSlYamlPath(path)) {
36
44
  continue;
37
45
  }
38
- const parts = path.split('/');
39
- const connectionId = parts[1] ?? '';
46
+ const connectionId = path.split('/')[1] ?? '';
40
47
  if (!connectionId) {
41
48
  unresolvedPaths.push(path);
42
49
  continue;
43
50
  }
44
- if (parts[2] !== '_schema') {
45
- const fileName = parts.at(-1) ?? '';
46
- const sourceName = fileName.replace(/\.ya?ml$/, '');
47
- if (!sourceName) {
48
- unresolvedPaths.push(path);
49
- continue;
50
- }
51
- touched.set(`${connectionId}:${sourceName}`, { connectionId, sourceName });
52
- continue;
53
- }
51
+ pathsByConnection.set(connectionId, [...(pathsByConnection.get(connectionId) ?? []), path]);
52
+ }
53
+ for (const [connectionId, paths] of pathsByConnection) {
54
54
  const changedNames = changedSourceNames(input.beforeSourcesByConnection.get(connectionId) ?? [], input.afterSourcesByConnection.get(connectionId) ?? []);
55
55
  if (changedNames.length === 0) {
56
- unresolvedPaths.push(path);
56
+ unresolvedPaths.push(...paths);
57
57
  continue;
58
58
  }
59
59
  for (const sourceName of changedNames) {
@@ -55,6 +55,7 @@ export declare class IngestBundleRunner {
55
55
  private provenanceValidationTraceData;
56
56
  private wikiPageKeysFromPaths;
57
57
  private touchedSlSourcesFromPaths;
58
+ private touchedSlSourcePaths;
58
59
  private touchedSlSourcesFromActions;
59
60
  private wikiPageKeysFromActions;
60
61
  private uniqueWikiPageKeys;