@kaelio/ktx 0.11.0 → 0.12.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.
- package/assets/python/{kaelio_ktx-0.11.0-py3-none-any.whl → kaelio_ktx-0.12.0-py3-none-any.whl} +0 -0
- package/assets/python/manifest.json +4 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/admin.js +1 -1
- package/dist/clack.d.ts +16 -0
- package/dist/clack.js +37 -6
- package/dist/claude-code-prompt-caching.js +1 -1
- package/dist/cli-program.js +3 -3
- package/dist/cli-runtime.js +2 -2
- package/dist/commands/connection-commands.js +1 -1
- package/dist/commands/ingest-commands.js +4 -4
- package/dist/commands/mcp-commands.js +12 -12
- package/dist/commands/runtime-commands.js +4 -4
- package/dist/commands/setup-commands.js +6 -5
- package/dist/commands/sl-commands.js +1 -1
- package/dist/commands/sql-commands.js +1 -1
- package/dist/commands/status-commands.js +1 -1
- package/dist/connection.js +1 -1
- package/dist/connectors/clickhouse/connector.js +1 -1
- package/dist/connectors/mysql/connector.js +1 -1
- package/dist/connectors/snowflake/connector.d.ts +1 -1
- package/dist/connectors/sqlite/connector.js +2 -25
- package/dist/connectors/sqlserver/connector.js +3 -3
- package/dist/context/connections/connection-type.d.ts +1 -1
- package/dist/context/connections/read-only-sql.d.ts +1 -0
- package/dist/context/connections/read-only-sql.js +116 -2
- package/dist/context/core/git.service.d.ts +23 -0
- package/dist/context/core/git.service.js +71 -8
- package/dist/context/ingest/adapters/historic-sql/projection.js +2 -1
- package/dist/context/ingest/adapters/looker/client.js +7 -2
- package/dist/context/ingest/adapters/looker/factory.d.ts +8 -1
- package/dist/context/ingest/adapters/looker/factory.js +9 -0
- package/dist/context/ingest/adapters/looker/mapping.js +1 -1
- package/dist/context/ingest/adapters/looker/types.d.ts +1 -1
- package/dist/context/ingest/adapters/metabase/client.d.ts +1 -1
- package/dist/context/ingest/adapters/metabase/client.js +1 -1
- package/dist/context/ingest/adapters/metabase/local-metabase.adapter.js +1 -1
- package/dist/context/ingest/adapters/metabase/mapping.js +6 -6
- package/dist/context/ingest/artifact-gates.d.ts +2 -6
- package/dist/context/ingest/artifact-gates.js +5 -47
- package/dist/context/ingest/constrained-repair.d.ts +55 -0
- package/dist/context/ingest/constrained-repair.js +167 -0
- package/dist/context/ingest/final-gate-repair.d.ts +9 -11
- package/dist/context/ingest/final-gate-repair.js +40 -128
- package/dist/context/ingest/finalization-scope.d.ts +1 -1
- package/dist/context/ingest/finalization-scope.js +15 -15
- package/dist/context/ingest/ingest-bundle.runner.d.ts +1 -0
- package/dist/context/ingest/ingest-bundle.runner.js +101 -67
- package/dist/context/ingest/isolated-diff/patch-integrator.d.ts +6 -13
- package/dist/context/ingest/isolated-diff/patch-integrator.js +32 -109
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.d.ts +8 -9
- package/dist/context/ingest/isolated-diff/textual-conflict-resolver.js +63 -141
- package/dist/context/ingest/local-bundle-runtime.d.ts +2 -0
- package/dist/context/ingest/local-bundle-runtime.js +9 -10
- package/dist/context/ingest/local-ingest.d.ts +2 -0
- package/dist/context/ingest/local-ingest.js +2 -0
- package/dist/context/ingest/memory-flow/view-model.js +1 -1
- package/dist/context/ingest/stages/stage-3-work-units.d.ts +2 -6
- package/dist/context/ingest/stages/stage-3-work-units.js +2 -1
- package/dist/context/ingest/stages/validate-wu-sources.d.ts +7 -1
- package/dist/context/ingest/stages/validate-wu-sources.js +109 -4
- package/dist/context/ingest/tools/warehouse-verification/create-warehouse-verification-tools.d.ts +2 -0
- package/dist/context/ingest/tools/warehouse-verification/create-warehouse-verification-tools.js +1 -1
- package/dist/context/ingest/tools/warehouse-verification/discover-data.tool.js +3 -3
- package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.d.ts +3 -1
- package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.js +15 -1
- package/dist/context/llm/ai-sdk-runtime.js +2 -2
- package/dist/context/llm/claude-code-runtime.js +1 -1
- package/dist/context/llm/local-config.js +1 -1
- package/dist/context/llm/runtime-tools.js +2 -2
- package/dist/context/mcp/context-tools.js +7 -7
- package/dist/context/mcp/local-project-ports.js +23 -54
- package/dist/context/memory/local-memory.js +4 -1
- package/dist/context/memory/memory-agent.service.js +1 -1
- package/dist/context/project/config.d.ts +11 -4
- package/dist/context/project/config.js +85 -30
- package/dist/context/project/driver-schemas.js +1 -1
- package/dist/context/project/mappings-yaml-schema.js +2 -2
- package/dist/context/project/project.js +12 -4
- package/dist/context/scan/description-generation.js +4 -4
- package/dist/context/scan/local-enrichment-artifacts.js +2 -1
- package/dist/context/scan/local-scan.js +2 -2
- package/dist/context/scan/local-structural-artifacts.js +5 -5
- package/dist/context/scan/relationship-benchmark-report.js +1 -1
- package/dist/context/scan/relationship-discovery.js +3 -3
- package/dist/context/scan/relationship-llm-proposal.js +3 -3
- package/dist/context/sl/local-query.js +3 -33
- package/dist/context/sl/local-sl.d.ts +0 -8
- package/dist/context/sl/local-sl.js +44 -69
- package/dist/context/sl/semantic-layer.service.d.ts +25 -8
- package/dist/context/sl/semantic-layer.service.js +109 -56
- package/dist/context/sl/source-files.d.ts +46 -0
- package/dist/context/sl/source-files.js +131 -0
- package/dist/context/sl/tools/base-semantic-layer.tool.d.ts +2 -2
- package/dist/context/sl/tools/base-semantic-layer.tool.js +2 -7
- package/dist/context/sl/tools/sl-edit-source.tool.js +10 -8
- package/dist/context/sl/tools/sl-warehouse-validation.js +55 -27
- package/dist/context/sl/tools/sl-write-source.tool.js +12 -9
- package/dist/context/sql-analysis/dialect.d.ts +2 -0
- package/dist/context/sql-analysis/dialect.js +20 -0
- package/dist/context/tools/base-tool.d.ts +6 -19
- package/dist/context/tools/base-tool.js +0 -14
- package/dist/context-build-view.js +5 -5
- package/dist/database-tree-picker.js +18 -3
- package/dist/demo-assets.js +0 -1
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +31 -23
- package/dist/errors.d.ts +31 -0
- package/dist/errors.js +44 -0
- package/dist/ingest.d.ts +1 -1
- package/dist/ingest.js +8 -2
- package/dist/io/symbols.d.ts +2 -0
- package/dist/io/symbols.js +2 -0
- package/dist/io/tty.d.ts +8 -0
- package/dist/io/tty.js +16 -0
- package/dist/llm/embedding-health.js +1 -1
- package/dist/llm/embedding-provider.js +3 -3
- package/dist/llm/model-provider.js +1 -1
- package/dist/local-adapters.d.ts +1 -0
- package/dist/local-adapters.js +2 -2
- package/dist/local-scan-connectors.js +1 -1
- package/dist/managed-local-embeddings.js +17 -8
- package/dist/managed-mcp-daemon.js +3 -3
- package/dist/managed-python-command.d.ts +7 -0
- package/dist/managed-python-command.js +34 -8
- package/dist/managed-python-daemon.js +2 -2
- package/dist/managed-python-http.js +3 -3
- package/dist/managed-python-runtime.d.ts +30 -1
- package/dist/managed-python-runtime.js +134 -18
- package/dist/managed-uv-release.d.ts +7 -0
- package/dist/managed-uv-release.js +11 -0
- package/dist/mcp-http-server.js +4 -4
- package/dist/mcp-server-factory.js +3 -3
- package/dist/mcp-stdio-server.js +1 -1
- package/dist/memory-flow-hud.js +2 -2
- package/dist/next-steps.js +2 -2
- package/dist/prompt-navigation.d.ts +17 -0
- package/dist/prompt-navigation.js +49 -3
- package/dist/prompts/memory_agent_bundle_ingest_work_unit.md +2 -2
- package/dist/prompts/memory_agent_external_ingest.md +2 -2
- package/dist/public-ingest-copy.js +1 -1
- package/dist/public-ingest.js +3 -3
- package/dist/release-version.js +1 -1
- package/dist/runtime-requirements.js +1 -1
- package/dist/runtime.js +9 -9
- package/dist/scan.js +1 -1
- package/dist/setup-agents.js +21 -30
- package/dist/setup-banner.d.ts +20 -0
- package/dist/setup-banner.js +39 -0
- package/dist/setup-context.js +24 -15
- package/dist/setup-databases.js +31 -59
- package/dist/setup-demo-tour.js +12 -8
- package/dist/setup-embeddings.js +9 -9
- package/dist/setup-interrupt.js +1 -1
- package/dist/setup-models.d.ts +4 -1
- package/dist/setup-models.js +54 -28
- package/dist/setup-project.js +29 -5
- package/dist/setup-prompts.js +16 -1
- package/dist/setup-ready-menu.js +1 -1
- package/dist/setup-sources.js +27 -7
- package/dist/setup.js +13 -13
- package/dist/skills/analytics/SKILL.md +3 -3
- package/dist/skills/dbt_ingest/SKILL.md +3 -3
- package/dist/skills/looker_ingest/SKILL.md +3 -3
- package/dist/skills/lookml_ingest/SKILL.md +7 -7
- package/dist/skills/metabase_ingest/SKILL.md +4 -4
- package/dist/skills/metricflow_ingest/SKILL.md +15 -15
- package/dist/skills/notion_synthesize/SKILL.md +1 -1
- package/dist/skills/sl/SKILL.md +3 -3
- package/dist/skills/sl_capture/SKILL.md +1 -1
- package/dist/skills/wiki_capture/SKILL.md +1 -1
- package/dist/source-mapping.js +1 -1
- package/dist/startup-profile.js +1 -1
- package/dist/status-project.d.ts +0 -2
- package/dist/status-project.js +4 -6
- package/dist/telemetry/events.d.ts +1 -1
- package/dist/telemetry/exception.js +14 -0
- package/dist/text-ingest.js +1 -1
- package/dist/tree-picker-tui.d.ts +0 -1
- package/dist/tree-picker-tui.js +2 -3
- package/package.json +1 -1
|
@@ -38,6 +38,19 @@ export async function integrateWorkUnitPatch(input) {
|
|
|
38
38
|
touchedPaths,
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
+
// Repair and resolution success is decided by this check, not by whether
|
|
42
|
+
// the repair agent edited files: the gates re-run over the union of the
|
|
43
|
+
// patch's paths and everything the agent changed.
|
|
44
|
+
const verifyAppliedTree = async (changedPaths) => {
|
|
45
|
+
const paths = [...new Set([...touchedPaths, ...changedPaths])].sort();
|
|
46
|
+
try {
|
|
47
|
+
await input.validateAppliedTree(paths);
|
|
48
|
+
return { ok: true };
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return { ok: false, reason: errorMessage(error) };
|
|
52
|
+
}
|
|
53
|
+
};
|
|
41
54
|
try {
|
|
42
55
|
await traceTimed(input.trace, 'integration', 'patch_apply', { unitKey: input.unitKey, patchPath: input.patchPath, touchedPaths }, async () => {
|
|
43
56
|
await input.integrationGit.applyPatchFile3WayIndex(input.patchPath);
|
|
@@ -67,6 +80,7 @@ export async function integrateWorkUnitPatch(input) {
|
|
|
67
80
|
patchPath: input.patchPath,
|
|
68
81
|
touchedPaths,
|
|
69
82
|
reason,
|
|
83
|
+
verify: verifyAppliedTree,
|
|
70
84
|
});
|
|
71
85
|
if (textualResolution.status === 'failed') {
|
|
72
86
|
if (preApplyHead) {
|
|
@@ -79,113 +93,37 @@ export async function integrateWorkUnitPatch(input) {
|
|
|
79
93
|
textualResolution,
|
|
80
94
|
};
|
|
81
95
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
catch (semanticError) {
|
|
88
|
-
const reason = errorMessage(semanticError);
|
|
89
|
-
await input.trace.event('error', 'integration', 'patch_semantic_conflict_after_textual_resolution', {
|
|
96
|
+
if (textualResolution.changedPaths.length === 0) {
|
|
97
|
+
// The resolver declared the patch redundant and the gates verified the
|
|
98
|
+
// current tree: the integration worktree already represents this work
|
|
99
|
+
// unit's content (e.g. a duplicate page created by another work unit).
|
|
100
|
+
await input.trace.event('debug', 'integration', 'patch_subsumed_after_textual_resolution', {
|
|
90
101
|
unitKey: input.unitKey,
|
|
91
102
|
patchPath: input.patchPath,
|
|
92
|
-
touchedPaths
|
|
93
|
-
|
|
103
|
+
touchedPaths,
|
|
104
|
+
attempts: textualResolution.attempts,
|
|
94
105
|
});
|
|
95
|
-
// A textual conflict and a semantic-gate failure can co-occur: the resolver
|
|
96
|
-
// reconciles the text but can leave wiki sl_refs pointing at measures the
|
|
97
|
-
// merged source no longer defines. Recover via the same gate repair the
|
|
98
|
-
// clean-apply branch uses, instead of hard-failing the whole job.
|
|
99
|
-
if (input.repairGateFailure) {
|
|
100
|
-
const gateRepair = await input.repairGateFailure({
|
|
101
|
-
unitKey: input.unitKey,
|
|
102
|
-
patchPath: input.patchPath,
|
|
103
|
-
touchedPaths: textualResolution.changedPaths,
|
|
104
|
-
reason,
|
|
105
|
-
});
|
|
106
|
-
if (gateRepair.status !== 'failed') {
|
|
107
|
-
// The resolver wrote its merge to the worktree (unstaged); the repair
|
|
108
|
-
// edited a subset on top. Commit the union so neither is dropped.
|
|
109
|
-
const resolvedAndRepairedPaths = [
|
|
110
|
-
...new Set([...textualResolution.changedPaths, ...gateRepair.changedPaths]),
|
|
111
|
-
].sort();
|
|
112
|
-
try {
|
|
113
|
-
await traceTimed(input.trace, 'integration', 'semantic_gate_after_gate_repair', { unitKey: input.unitKey, touchedPaths: gateRepair.changedPaths }, async () => {
|
|
114
|
-
await input.validateAppliedTree(gateRepair.changedPaths);
|
|
115
|
-
});
|
|
116
|
-
const commit = await input.integrationGit.commitFiles(resolvedAndRepairedPaths, `ingest: resolve WorkUnit ${input.unitKey} conflict`, input.author.name, input.author.email);
|
|
117
|
-
if (commit.created) {
|
|
118
|
-
await input.trace.event('debug', 'integration', 'patch_accepted_after_textual_resolution', {
|
|
119
|
-
unitKey: input.unitKey,
|
|
120
|
-
commitSha: commit.commitHash,
|
|
121
|
-
touchedPaths: resolvedAndRepairedPaths,
|
|
122
|
-
attempts: textualResolution.attempts,
|
|
123
|
-
gateRepairAttempts: gateRepair.attempts,
|
|
124
|
-
});
|
|
125
|
-
return {
|
|
126
|
-
status: 'accepted',
|
|
127
|
-
commitSha: commit.commitHash,
|
|
128
|
-
touchedPaths: resolvedAndRepairedPaths,
|
|
129
|
-
textualResolution,
|
|
130
|
-
gateRepair,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
catch (repairValidationError) {
|
|
135
|
-
if (preApplyHead) {
|
|
136
|
-
await input.integrationGit.resetHardTo(preApplyHead);
|
|
137
|
-
}
|
|
138
|
-
await input.trace.event('error', 'integration', 'patch_semantic_conflict_after_textual_resolution', {
|
|
139
|
-
unitKey: input.unitKey,
|
|
140
|
-
patchPath: input.patchPath,
|
|
141
|
-
touchedPaths: gateRepair.changedPaths,
|
|
142
|
-
reason: errorMessage(repairValidationError),
|
|
143
|
-
});
|
|
144
|
-
return {
|
|
145
|
-
status: 'semantic_conflict',
|
|
146
|
-
reason: errorMessage(repairValidationError),
|
|
147
|
-
touchedPaths: gateRepair.changedPaths,
|
|
148
|
-
textualResolution,
|
|
149
|
-
gateRepair,
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
if (preApplyHead) {
|
|
154
|
-
await input.integrationGit.resetHardTo(preApplyHead);
|
|
155
|
-
}
|
|
156
|
-
return {
|
|
157
|
-
status: 'semantic_conflict',
|
|
158
|
-
reason: gateRepair.status === 'failed' ? gateRepair.reason : reason,
|
|
159
|
-
touchedPaths: textualResolution.changedPaths,
|
|
160
|
-
textualResolution,
|
|
161
|
-
gateRepair,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
if (preApplyHead) {
|
|
165
|
-
await input.integrationGit.resetHardTo(preApplyHead);
|
|
166
|
-
}
|
|
167
106
|
return {
|
|
168
|
-
status: '
|
|
169
|
-
|
|
170
|
-
touchedPaths:
|
|
107
|
+
status: 'accepted',
|
|
108
|
+
commitSha: preApplyHead ?? '',
|
|
109
|
+
touchedPaths: [],
|
|
171
110
|
textualResolution,
|
|
172
111
|
};
|
|
173
112
|
}
|
|
174
113
|
const commit = await input.integrationGit.commitFiles(textualResolution.changedPaths, `ingest: resolve WorkUnit ${input.unitKey} conflict`, input.author.name, input.author.email);
|
|
175
114
|
if (!commit.created) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const noChangeReason = 'textual resolver produced no committable changes';
|
|
180
|
-
await input.trace.event('error', 'integration', 'textual_conflict_resolver_noop', {
|
|
115
|
+
// The resolver's writes left the tree byte-identical to the accepted
|
|
116
|
+
// state, and the gates verified it — the patch is represented already.
|
|
117
|
+
await input.trace.event('debug', 'integration', 'patch_subsumed_after_textual_resolution', {
|
|
181
118
|
unitKey: input.unitKey,
|
|
182
119
|
patchPath: input.patchPath,
|
|
183
120
|
touchedPaths: textualResolution.changedPaths,
|
|
121
|
+
attempts: textualResolution.attempts,
|
|
184
122
|
});
|
|
185
123
|
return {
|
|
186
|
-
status: '
|
|
187
|
-
|
|
188
|
-
touchedPaths:
|
|
124
|
+
status: 'accepted',
|
|
125
|
+
commitSha: preApplyHead ?? '',
|
|
126
|
+
touchedPaths: [],
|
|
189
127
|
textualResolution,
|
|
190
128
|
};
|
|
191
129
|
}
|
|
@@ -221,6 +159,7 @@ export async function integrateWorkUnitPatch(input) {
|
|
|
221
159
|
patchPath: input.patchPath,
|
|
222
160
|
touchedPaths,
|
|
223
161
|
reason,
|
|
162
|
+
verify: verifyAppliedTree,
|
|
224
163
|
});
|
|
225
164
|
if (gateRepair.status === 'failed') {
|
|
226
165
|
if (preApplyHead) {
|
|
@@ -233,22 +172,6 @@ export async function integrateWorkUnitPatch(input) {
|
|
|
233
172
|
gateRepair,
|
|
234
173
|
};
|
|
235
174
|
}
|
|
236
|
-
try {
|
|
237
|
-
await traceTimed(input.trace, 'integration', 'semantic_gate_after_gate_repair', { unitKey: input.unitKey, touchedPaths: gateRepair.changedPaths }, async () => {
|
|
238
|
-
await input.validateAppliedTree(gateRepair.changedPaths);
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
catch (repairValidationError) {
|
|
242
|
-
if (preApplyHead) {
|
|
243
|
-
await input.integrationGit.resetHardTo(preApplyHead);
|
|
244
|
-
}
|
|
245
|
-
return {
|
|
246
|
-
status: 'semantic_conflict',
|
|
247
|
-
reason: errorMessage(repairValidationError),
|
|
248
|
-
touchedPaths: gateRepair.changedPaths,
|
|
249
|
-
gateRepair,
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
175
|
const commit = await input.integrationGit.commitFiles(gateRepair.changedPaths, `ingest: repair WorkUnit ${input.unitKey} gates`, input.author.name, input.author.email);
|
|
253
176
|
if (!commit.created) {
|
|
254
177
|
if (preApplyHead) {
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import type { AgentRunnerPort } from '../../../context/llm/runtime-port.js';
|
|
2
|
+
import type { ConstrainedRepairResult, RepairVerification } from '../constrained-repair.js';
|
|
2
3
|
import type { IngestTraceWriter } from '../ingest-trace.js';
|
|
3
|
-
export type TextualConflictResolutionResult =
|
|
4
|
-
status: 'repaired';
|
|
5
|
-
attempts: number;
|
|
6
|
-
changedPaths: string[];
|
|
7
|
-
} | {
|
|
8
|
-
status: 'failed';
|
|
9
|
-
attempts: number;
|
|
10
|
-
reason: string;
|
|
11
|
-
};
|
|
4
|
+
export type TextualConflictResolutionResult = ConstrainedRepairResult;
|
|
12
5
|
export interface ResolveTextualConflictInput {
|
|
13
6
|
agentRunner: AgentRunnerPort;
|
|
14
7
|
workdir: string;
|
|
@@ -17,6 +10,12 @@ export interface ResolveTextualConflictInput {
|
|
|
17
10
|
touchedPaths: string[];
|
|
18
11
|
trace: IngestTraceWriter;
|
|
19
12
|
reason: string;
|
|
13
|
+
/**
|
|
14
|
+
* Re-runs the artifact gates against the current worktree. A resolution —
|
|
15
|
+
* including an explicit no-change declaration for a redundant patch —
|
|
16
|
+
* counts as successful only when this passes.
|
|
17
|
+
*/
|
|
18
|
+
verify(changedPaths: string[]): Promise<RepairVerification>;
|
|
20
19
|
maxAttempts?: number;
|
|
21
20
|
stepBudget?: number;
|
|
22
21
|
abortSignal?: AbortSignal;
|
|
@@ -1,58 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { dirname, join } from 'node:path';
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
3
2
|
import { z } from 'zod';
|
|
4
|
-
import {
|
|
5
|
-
const readIntegrationFileSchema = z.object({
|
|
6
|
-
path: z.string().min(1),
|
|
7
|
-
});
|
|
8
|
-
const writeIntegrationFileSchema = z.object({
|
|
9
|
-
path: z.string().min(1),
|
|
10
|
-
content: z.string(),
|
|
11
|
-
});
|
|
12
|
-
const deleteIntegrationFileSchema = z.object({
|
|
13
|
-
path: z.string().min(1),
|
|
14
|
-
});
|
|
15
|
-
function normalizeRepoPath(path) {
|
|
16
|
-
const normalized = path.replace(/\\/g, '/').replace(/^\/+/, '');
|
|
17
|
-
const parts = normalized.split('/').filter((part) => part.length > 0);
|
|
18
|
-
if (parts.length === 0 || parts.some((part) => part === '.' || part === '..')) {
|
|
19
|
-
throw new Error(`resolver path must be a repository-relative path: ${path}`);
|
|
20
|
-
}
|
|
21
|
-
return parts.join('/');
|
|
22
|
-
}
|
|
23
|
-
function assertAllowedPath(path, allowedPaths) {
|
|
24
|
-
const normalized = normalizeRepoPath(path);
|
|
25
|
-
if (!allowedPaths.has(normalized)) {
|
|
26
|
-
throw new Error(`resolver path not allowed: ${normalized}`);
|
|
27
|
-
}
|
|
28
|
-
return normalized;
|
|
29
|
-
}
|
|
30
|
-
async function readOptionalFile(path) {
|
|
31
|
-
try {
|
|
32
|
-
return { exists: true, content: await readFile(path, 'utf-8') };
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
|
|
36
|
-
return { exists: false, content: '' };
|
|
37
|
-
}
|
|
38
|
-
throw error;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
3
|
+
import { buildDeleteRepairFileTool, runConstrainedRepairLoop } from '../constrained-repair.js';
|
|
41
4
|
function buildResolverSystemPrompt() {
|
|
42
5
|
return `<role>
|
|
43
|
-
You repair one failed
|
|
6
|
+
You repair one failed ktx isolated-diff patch inside the integration worktree.
|
|
44
7
|
</role>
|
|
45
8
|
|
|
46
9
|
<rules>
|
|
47
10
|
- Preserve accepted integration content that is unrelated to the failed patch.
|
|
48
11
|
- Incorporate the failed patch only when the patch evidence is compatible with the current file.
|
|
12
|
+
- If the current file already represents everything the failed patch contributes (for example a
|
|
13
|
+
duplicate page created by another work unit), call declare_patch_redundant instead of editing.
|
|
49
14
|
- Edit only paths exposed by the resolver tools.
|
|
50
15
|
- Prefer the smallest text edit that makes the composed artifact coherent.
|
|
51
16
|
- Do not create new facts that are absent from the current file or failed patch.
|
|
52
|
-
- Stop after writing the repaired file content.
|
|
17
|
+
- Stop after writing the repaired file content or declaring the patch redundant.
|
|
53
18
|
</rules>`;
|
|
54
19
|
}
|
|
55
20
|
function buildResolverUserPrompt(input) {
|
|
21
|
+
const previousFailureBlock = input.previousFailure
|
|
22
|
+
? `\nPrevious attempt did not pass the artifact gates:\n${input.previousFailure}\n`
|
|
23
|
+
: '';
|
|
56
24
|
return `Repair isolated-diff textual conflict.
|
|
57
25
|
|
|
58
26
|
WorkUnit: ${input.unitKey}
|
|
@@ -63,11 +31,18 @@ ${input.touchedPaths.map((path) => `- ${path}`).join('\n')}
|
|
|
63
31
|
|
|
64
32
|
Git apply failure:
|
|
65
33
|
${input.reason}
|
|
66
|
-
|
|
67
|
-
Use read_failed_patch first. Then read the touched integration files
|
|
68
|
-
repaired content,
|
|
34
|
+
${previousFailureBlock}
|
|
35
|
+
Use read_failed_patch first. Then read the touched integration files and either
|
|
36
|
+
write the repaired content or, when the patch adds nothing the current files do
|
|
37
|
+
not already cover, call declare_patch_redundant. Then stop.`;
|
|
69
38
|
}
|
|
70
|
-
function
|
|
39
|
+
function buildResolverExtraTools(input) {
|
|
40
|
+
const declareSchema = z.object({
|
|
41
|
+
reason: z
|
|
42
|
+
.string()
|
|
43
|
+
.min(1)
|
|
44
|
+
.describe('Why the integration tree already represents everything this patch contributes.'),
|
|
45
|
+
});
|
|
71
46
|
return {
|
|
72
47
|
read_failed_patch: {
|
|
73
48
|
name: 'read_failed_patch',
|
|
@@ -81,111 +56,58 @@ function buildToolSet(input) {
|
|
|
81
56
|
};
|
|
82
57
|
},
|
|
83
58
|
},
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
59
|
+
...buildDeleteRepairFileTool(input.context),
|
|
60
|
+
declare_patch_redundant: {
|
|
61
|
+
name: 'declare_patch_redundant',
|
|
62
|
+
description: 'Declare that the failed patch needs no integration because the current worktree already ' +
|
|
63
|
+
'represents its content (for example a duplicate page created by another work unit).',
|
|
64
|
+
inputSchema: declareSchema,
|
|
65
|
+
execute: async ({ reason }) => {
|
|
66
|
+
input.context.declareNoChange(reason);
|
|
91
67
|
return {
|
|
92
|
-
markdown:
|
|
93
|
-
structured: {
|
|
94
|
-
};
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
write_integration_file: {
|
|
98
|
-
name: 'write_integration_file',
|
|
99
|
-
description: 'Replace one allowed integration worktree file with repaired text content.',
|
|
100
|
-
inputSchema: writeIntegrationFileSchema,
|
|
101
|
-
execute: async ({ path, content }) => {
|
|
102
|
-
const normalized = assertAllowedPath(path, input.allowedPaths);
|
|
103
|
-
const fullPath = join(input.workdir, normalized);
|
|
104
|
-
await mkdir(dirname(fullPath), { recursive: true });
|
|
105
|
-
await writeFile(fullPath, content, 'utf-8');
|
|
106
|
-
input.editedPaths.add(normalized);
|
|
107
|
-
return {
|
|
108
|
-
markdown: `Wrote ${normalized}`,
|
|
109
|
-
structured: { path: normalized, bytes: Buffer.byteLength(content) },
|
|
110
|
-
};
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
delete_integration_file: {
|
|
114
|
-
name: 'delete_integration_file',
|
|
115
|
-
description: 'Delete one allowed integration worktree file when the failed patch proves the deletion is correct.',
|
|
116
|
-
inputSchema: deleteIntegrationFileSchema,
|
|
117
|
-
execute: async ({ path }) => {
|
|
118
|
-
const normalized = assertAllowedPath(path, input.allowedPaths);
|
|
119
|
-
await rm(join(input.workdir, normalized), { force: true });
|
|
120
|
-
input.editedPaths.add(normalized);
|
|
121
|
-
return {
|
|
122
|
-
markdown: `Deleted ${normalized}`,
|
|
123
|
-
structured: { path: normalized },
|
|
68
|
+
markdown: `Declared patch redundant: ${reason}`,
|
|
69
|
+
structured: { reason },
|
|
124
70
|
};
|
|
125
71
|
},
|
|
126
72
|
},
|
|
127
73
|
};
|
|
128
74
|
}
|
|
129
75
|
export async function resolveTextualConflict(input) {
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
76
|
+
const sortedTouchedPaths = [...input.touchedPaths].sort();
|
|
77
|
+
return runConstrainedRepairLoop({
|
|
78
|
+
agentRunner: input.agentRunner,
|
|
79
|
+
workdir: input.workdir,
|
|
80
|
+
allowedPaths: input.touchedPaths,
|
|
81
|
+
trace: input.trace,
|
|
82
|
+
tracePhase: 'resolver',
|
|
83
|
+
traceEventName: 'textual_conflict_resolver',
|
|
84
|
+
traceData: {
|
|
85
|
+
unitKey: input.unitKey,
|
|
86
|
+
patchPath: input.patchPath,
|
|
87
|
+
touchedPaths: sortedTouchedPaths,
|
|
88
|
+
reason: input.reason,
|
|
89
|
+
},
|
|
90
|
+
systemPrompt: buildResolverSystemPrompt(),
|
|
91
|
+
buildUserPrompt: ({ attempt, maxAttempts, previousFailure }) => buildResolverUserPrompt({
|
|
137
92
|
unitKey: input.unitKey,
|
|
138
93
|
patchPath: input.patchPath,
|
|
139
|
-
touchedPaths:
|
|
94
|
+
touchedPaths: sortedTouchedPaths,
|
|
95
|
+
reason: input.reason,
|
|
140
96
|
attempt,
|
|
141
97
|
maxAttempts,
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
patchPath: input.patchPath,
|
|
158
|
-
allowedPaths,
|
|
159
|
-
editedPaths,
|
|
160
|
-
}),
|
|
161
|
-
stepBudget,
|
|
162
|
-
telemetryTags: {
|
|
163
|
-
operationName: 'ingest-isolated-diff-textual-resolver',
|
|
164
|
-
source: input.trace.context.sourceKey,
|
|
165
|
-
jobId: input.trace.context.jobId,
|
|
166
|
-
unitKey: input.unitKey,
|
|
167
|
-
},
|
|
168
|
-
abortSignal: input.abortSignal,
|
|
169
|
-
}));
|
|
170
|
-
if (result.stopReason === 'error') {
|
|
171
|
-
lastFailure = result.error?.message ?? 'resolver agent loop errored';
|
|
172
|
-
await input.trace.event('error', 'resolver', 'textual_conflict_resolver_failed', traceData, result.error);
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
const changedPaths = [...editedPaths].sort();
|
|
176
|
-
if (changedPaths.length === 0) {
|
|
177
|
-
lastFailure = 'resolver completed without editing an allowed path';
|
|
178
|
-
await input.trace.event('error', 'resolver', 'textual_conflict_resolver_failed', {
|
|
179
|
-
...traceData,
|
|
180
|
-
reason: lastFailure,
|
|
181
|
-
});
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
await input.trace.event('debug', 'resolver', 'textual_conflict_resolver_repaired', {
|
|
185
|
-
...traceData,
|
|
186
|
-
changedPaths,
|
|
187
|
-
});
|
|
188
|
-
return { status: 'repaired', attempts: attempt, changedPaths };
|
|
189
|
-
}
|
|
190
|
-
return { status: 'failed', attempts: maxAttempts, reason: lastFailure };
|
|
98
|
+
previousFailure,
|
|
99
|
+
}),
|
|
100
|
+
buildExtraTools: (context) => buildResolverExtraTools({ patchPath: input.patchPath, context }),
|
|
101
|
+
verify: input.verify,
|
|
102
|
+
noChangeFailureReason: 'resolver completed without editing an allowed path or declaring the patch redundant',
|
|
103
|
+
telemetryTags: {
|
|
104
|
+
operationName: 'ingest-isolated-diff-textual-resolver',
|
|
105
|
+
source: input.trace.context.sourceKey,
|
|
106
|
+
jobId: input.trace.context.jobId,
|
|
107
|
+
unitKey: input.unitKey,
|
|
108
|
+
},
|
|
109
|
+
maxAttempts: input.maxAttempts,
|
|
110
|
+
stepBudget: input.stepBudget ?? 12,
|
|
111
|
+
abortSignal: input.abortSignal,
|
|
112
|
+
});
|
|
191
113
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { KtxSqlQueryExecutorPort } from '../../context/connections/query-executor.js';
|
|
2
|
+
import type { SqlAnalysisPort } from '../../context/sql-analysis/ports.js';
|
|
2
3
|
import type { KtxLogger } from '../../context/core/config.js';
|
|
3
4
|
import type { KtxSemanticLayerComputePort } from '../../context/daemon/semantic-layer-compute.js';
|
|
4
5
|
import { createLocalKtxLlmRuntimeFromConfig } from '../../context/llm/local-config.js';
|
|
@@ -20,6 +21,7 @@ export interface CreateLocalBundleIngestRuntimeOptions {
|
|
|
20
21
|
memoryModel?: string;
|
|
21
22
|
semanticLayerCompute?: KtxSemanticLayerComputePort;
|
|
22
23
|
queryExecutor?: KtxSqlQueryExecutorPort;
|
|
24
|
+
sqlAnalysis?: SqlAnalysisPort;
|
|
23
25
|
jobIdFactory?: () => string;
|
|
24
26
|
logger?: KtxLogger;
|
|
25
27
|
embeddingProvider?: KtxEmbeddingProvider | null;
|
|
@@ -52,7 +52,7 @@ import { SourceAdapterRegistry } from './source-adapter-registry.js';
|
|
|
52
52
|
import { SqliteBundleIngestStore } from './sqlite-bundle-ingest-store.js';
|
|
53
53
|
const promptsDir = fileURLToPath(new URL('../../prompts', import.meta.url));
|
|
54
54
|
const skillsDir = fileURLToPath(new URL('../../skills', import.meta.url));
|
|
55
|
-
const LOCAL_AUTHOR = { name: '
|
|
55
|
+
const LOCAL_AUTHOR = { name: 'ktx Local', email: 'local@ktx.local' };
|
|
56
56
|
const LOCAL_SHAPE_WARNING = 'Local ingest validates semantic-layer YAML shape only.';
|
|
57
57
|
const INGEST_TRACE_LEVELS = new Set(['error', 'info', 'debug', 'trace']);
|
|
58
58
|
function ingestTraceLevelFromEnv(env = process.env) {
|
|
@@ -169,7 +169,8 @@ class LocalSlPythonPort {
|
|
|
169
169
|
}
|
|
170
170
|
class LocalShapeOnlySlValidator {
|
|
171
171
|
validateParsedSource(sourceName, parsed) {
|
|
172
|
-
const
|
|
172
|
+
const fields = (parsed ?? {});
|
|
173
|
+
const isOverlay = fields.table == null && fields.sql == null;
|
|
173
174
|
const result = (isOverlay ? sourceOverlaySchema : sourceDefinitionSchema).safeParse(parsed);
|
|
174
175
|
return result.success
|
|
175
176
|
? { errors: [], warnings: [LOCAL_SHAPE_WARNING] }
|
|
@@ -200,16 +201,12 @@ class LocalShapeOnlySlValidator {
|
|
|
200
201
|
}
|
|
201
202
|
}
|
|
202
203
|
async validateSingleSource(deps, connectionId, sourceName) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
content = file.content;
|
|
207
|
-
}
|
|
208
|
-
catch (error) {
|
|
209
|
-
return this.validateComposedSource(deps, connectionId, sourceName, error);
|
|
204
|
+
const file = await deps.semanticLayerService.readSourceFile(connectionId, sourceName);
|
|
205
|
+
if (!file) {
|
|
206
|
+
return this.validateComposedSource(deps, connectionId, sourceName, 'no standalone or overlay file found');
|
|
210
207
|
}
|
|
211
208
|
try {
|
|
212
|
-
const parsed = YAML.parse(content);
|
|
209
|
+
const parsed = YAML.parse(file.content);
|
|
213
210
|
return this.validateParsedSource(sourceName, parsed);
|
|
214
211
|
}
|
|
215
212
|
catch (error) {
|
|
@@ -439,6 +436,7 @@ class LocalIngestToolsetFactory {
|
|
|
439
436
|
const slDiscoverTool = new SlDiscoverTool(slDeps, { maxSources: 25, minRrfScore: 0, maxDetailedSources: 5 });
|
|
440
437
|
const warehouseVerificationTools = createWarehouseVerificationTools({
|
|
441
438
|
connections: deps.connections,
|
|
439
|
+
...(deps.sqlAnalysis ? { sqlAnalysis: deps.sqlAnalysis } : {}),
|
|
442
440
|
fallbackFileStore: deps.project.fileStore,
|
|
443
441
|
wikiSearchTool,
|
|
444
442
|
slDiscoverTool,
|
|
@@ -556,6 +554,7 @@ export function createLocalBundleIngestRuntime(options) {
|
|
|
556
554
|
authorResolver: new LocalAuthorResolver(),
|
|
557
555
|
slSourcesRepository,
|
|
558
556
|
connections,
|
|
557
|
+
...(options.sqlAnalysis ? { sqlAnalysis: options.sqlAnalysis } : {}),
|
|
559
558
|
contextStore,
|
|
560
559
|
embedding,
|
|
561
560
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { KtxSqlQueryExecutorPort } from '../../context/connections/query-executor.js';
|
|
2
|
+
import type { SqlAnalysisPort } from '../../context/sql-analysis/ports.js';
|
|
2
3
|
import type { KtxLogger } from '../../context/core/config.js';
|
|
3
4
|
import type { KtxSemanticLayerComputePort } from '../../context/daemon/semantic-layer-compute.js';
|
|
4
5
|
import type { AgentRunnerPort, KtxLlmRuntimePort } from '../../context/llm/runtime-port.js';
|
|
@@ -23,6 +24,7 @@ export interface RunLocalIngestOptions {
|
|
|
23
24
|
memoryModel?: string;
|
|
24
25
|
semanticLayerCompute?: KtxSemanticLayerComputePort;
|
|
25
26
|
queryExecutor?: KtxSqlQueryExecutorPort;
|
|
27
|
+
sqlAnalysis?: SqlAnalysisPort;
|
|
26
28
|
logger?: KtxLogger;
|
|
27
29
|
embeddingProvider?: import('../../llm/types.js').KtxEmbeddingProvider | null;
|
|
28
30
|
abortSignal?: AbortSignal;
|
|
@@ -102,6 +102,7 @@ export async function runLocalIngest(options) {
|
|
|
102
102
|
memoryModel: options.memoryModel,
|
|
103
103
|
semanticLayerCompute: options.semanticLayerCompute,
|
|
104
104
|
queryExecutor: options.queryExecutor,
|
|
105
|
+
sqlAnalysis: options.sqlAnalysis,
|
|
105
106
|
logger: options.logger,
|
|
106
107
|
embeddingProvider: options.embeddingProvider,
|
|
107
108
|
abortSignal: options.abortSignal,
|
|
@@ -259,6 +260,7 @@ export async function runLocalMetabaseIngest(options) {
|
|
|
259
260
|
memoryModel: options.memoryModel,
|
|
260
261
|
semanticLayerCompute: options.semanticLayerCompute,
|
|
261
262
|
queryExecutor: options.queryExecutor,
|
|
263
|
+
sqlAnalysis: options.sqlAnalysis,
|
|
262
264
|
logger: options.logger,
|
|
263
265
|
embeddingProvider: options.embeddingProvider,
|
|
264
266
|
abortSignal: options.abortSignal,
|
|
@@ -436,7 +436,7 @@ export function buildMemoryFlowViewModel(input) {
|
|
|
436
436
|
? [...new Set(sources.map((s) => humanizeAdapter(s.adapter)))].join(' + ')
|
|
437
437
|
: `${input.connectionId}/${input.adapter}`;
|
|
438
438
|
return {
|
|
439
|
-
title: `
|
|
439
|
+
title: `ktx memory flow ${titleSources} ${input.status}`,
|
|
440
440
|
subtitle: `Run ${input.runId} Sync ${input.syncId}`,
|
|
441
441
|
status: input.status,
|
|
442
442
|
activeLine: activeLine(input),
|
|
@@ -2,18 +2,15 @@ import type { KtxModelRole } from '../../../llm/types.js';
|
|
|
2
2
|
import type { AgentRunnerPort, KtxRuntimeToolSet, RunLoopMetrics } from '../../../context/llm/runtime-port.js';
|
|
3
3
|
import type { CaptureSession, MemoryAction } from '../../../context/memory/types.js';
|
|
4
4
|
import { type TouchedSlSource } from '../../../context/tools/touched-sl-sources.js';
|
|
5
|
+
import { type WuValidationResult } from './validate-wu-sources.js';
|
|
5
6
|
import type { WorkUnit } from '../types.js';
|
|
6
|
-
interface TouchedValidationResult {
|
|
7
|
-
invalidSources: string[];
|
|
8
|
-
validSources: string[];
|
|
9
|
-
}
|
|
10
7
|
export interface WorkUnitExecutionDeps {
|
|
11
8
|
sessionWorktreeGit: {
|
|
12
9
|
revParseHead(): Promise<string | null>;
|
|
13
10
|
};
|
|
14
11
|
agentRunner: AgentRunnerPort;
|
|
15
12
|
validateWikiRefs?: (actions: MemoryAction[]) => Promise<string[]>;
|
|
16
|
-
validateTouchedSources: (touched: TouchedSlSource[]) => Promise<
|
|
13
|
+
validateTouchedSources: (touched: TouchedSlSource[]) => Promise<WuValidationResult>;
|
|
17
14
|
resetHardTo: (targetSha: string) => Promise<void>;
|
|
18
15
|
buildSystemPrompt: (wu: WorkUnit) => string;
|
|
19
16
|
buildUserPrompt: (wu: WorkUnit) => string;
|
|
@@ -45,4 +42,3 @@ export interface WorkUnitOutcome {
|
|
|
45
42
|
metrics?: RunLoopMetrics;
|
|
46
43
|
}
|
|
47
44
|
export declare function executeWorkUnit(deps: WorkUnitExecutionDeps, wu: WorkUnit): Promise<WorkUnitOutcome>;
|
|
48
|
-
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isAbortError } from '../../core/abort.js';
|
|
2
2
|
import { listTouchedSlSources } from '../../../context/tools/touched-sl-sources.js';
|
|
3
|
+
import { formatInvalidWuSources } from './validate-wu-sources.js';
|
|
3
4
|
const MAX_WORK_UNIT_PROMPT_CHARS = 240_000;
|
|
4
5
|
export async function executeWorkUnit(deps, wu) {
|
|
5
6
|
const preSha = (await deps.sessionWorktreeGit.revParseHead()) ?? '';
|
|
@@ -97,7 +98,7 @@ export async function executeWorkUnit(deps, wu) {
|
|
|
97
98
|
// Spec: invalid SL writes reset the session worktree to the WU's pre-state, WU is marked failed,
|
|
98
99
|
// its files are absent from the Stage Index. Per-source surgical revert is the
|
|
99
100
|
// memory-agent pattern — NOT the bundle-ingest pattern.
|
|
100
|
-
return failWithReset(`sl_validate failed for: ${validation.invalidSources
|
|
101
|
+
return failWithReset(`sl_validate failed for: ${formatInvalidWuSources(validation.invalidSources)}`);
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
return {
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import type { SlValidationDeps } from '../../../context/sl/tools/sl-warehouse-validation.js';
|
|
2
2
|
import type { SlValidatorPort } from '../../../context/sl/sl-validator.port.js';
|
|
3
3
|
import type { TouchedSlSource } from '../../../context/tools/touched-sl-sources.js';
|
|
4
|
+
export interface InvalidWuSource {
|
|
5
|
+
/** `${connectionId}:${sourceName}` */
|
|
6
|
+
source: string;
|
|
7
|
+
errors: string[];
|
|
8
|
+
}
|
|
4
9
|
export interface WuValidationResult {
|
|
5
10
|
validSources: string[];
|
|
6
|
-
invalidSources:
|
|
11
|
+
invalidSources: InvalidWuSource[];
|
|
7
12
|
}
|
|
13
|
+
export declare function formatInvalidWuSources(invalid: InvalidWuSource[]): string;
|
|
8
14
|
export declare function validateWuTouchedSources(deps: SlValidationDeps & {
|
|
9
15
|
slValidator: SlValidatorPort<SlValidationDeps>;
|
|
10
16
|
}, touched: TouchedSlSource[]): Promise<WuValidationResult>;
|