Skip to content
20 changes: 13 additions & 7 deletions packages/opencode/src/altimate/native/altimate-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,15 @@ register("altimate_core.transpile", async (params) => {
// Post-process QUALIFY for targets that lack native support
const targetLower = params.to_dialect.toLowerCase()
if (QUALIFY_TARGETS.has(targetLower)) {
const translated =
(data.sql as string) || (data.translated_sql as string) || ""
if (translated && translated.toUpperCase().includes("QUALIFY")) {
const fixed = postprocessQualify(translated)
if ("sql" in data) {
// Rust returns transpiled_sql as string[] — use first element
const transpiled = Array.isArray(data.transpiled_sql)
? (data.transpiled_sql as string[])[0]
: (data.transpiled_sql as string) || (data.sql as string) || (data.translated_sql as string) || ""
if (transpiled && transpiled.toUpperCase().includes("QUALIFY")) {
const fixed = postprocessQualify(transpiled)
if (Array.isArray(data.transpiled_sql)) {
;(data.transpiled_sql as string[])[0] = fixed
} else if ("sql" in data) {
data.sql = fixed
} else {
data.translated_sql = fixed
Expand Down Expand Up @@ -326,12 +330,14 @@ register("altimate_core.query_pii", async (params) => {
}
})

// 19. altimate_core.resolve_term
// 19. altimate_core.resolve_term — returns array, must wrap
register("altimate_core.resolve_term", async (params) => {
try {
const schema = schemaOrEmpty(params.schema_path, params.schema_context)
const raw = core.resolveTerm(params.term, schema)
return ok(true, toData(raw))
// Rust returns an array of matches — wrap for consistent object shape
const matches = Array.isArray(raw) ? JSON.parse(JSON.stringify(raw)) : []
return ok(matches.length > 0, { matches })
} catch (e) {
return fail(e)
}
Expand Down
86 changes: 85 additions & 1 deletion packages/opencode/src/altimate/native/schema-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,94 @@
*
* Translates the bridge protocol's `schema_path` / `schema_context` parameters
* into altimate-core `Schema` objects.
*
* Tools pass `schema_context` in two possible formats:
*
* 1. **Flat format** (used by most tools):
* `{ "table_name": { "col_name": "TYPE", ... } }`
*
* 2. **SchemaDefinition format** (matches Rust struct):
* `{ "tables": { "table_name": { "columns": [{ "name": "col", "type": "TYPE" }] } } }`
*
* This module normalizes both formats into the SchemaDefinition format
* expected by `Schema.fromJson()`.
*/

import { Schema } from "@altimateai/altimate-core"

/**
* Detect whether a schema_context object is in flat format or SchemaDefinition format.
*
* Flat format: `{ "table_name": { "col_name": "TYPE" } }`
* SchemaDefinition format: has a `tables` key with nested structure.
*/
function isSchemaDefinitionFormat(ctx: Record<string, any>): boolean {
if (!("tables" in ctx) || typeof ctx.tables !== "object" || ctx.tables === null) {
return false
}
// Verify at least one value under `tables` looks like a table definition
// (has a `columns` array), not a flat column map like { "col": "TYPE" }.
// This prevents false positives when a flat schema has a table named "tables".
const values = Object.values(ctx.tables)
if (values.length === 0) return true // empty tables is valid SchemaDefinition
return values.some((v: any) => Array.isArray(v?.columns))
}

/**
* Convert flat schema format to SchemaDefinition format.
*
* Handles three input variants:
*
* 1. Flat map: `{ "customers": { "id": "INTEGER", "name": "VARCHAR" } }`
* 2. Array form: `{ "customers": [{ "name": "id", "data_type": "INTEGER" }] }`
* 3. Partial SD: `{ "customers": { "columns": [{ "name": "id", "type": "INTEGER" }] } }`
*
* Output: `{ "tables": { "customers": { "columns": [{ "name": "id", "type": "INTEGER" }, ...] } } }`
*/
function flatToSchemaDefinition(flat: Record<string, any>): Record<string, any> {
const tables: Record<string, any> = {}
for (const [tableName, colsOrDef] of Object.entries(flat)) {
if (colsOrDef === null || colsOrDef === undefined) continue

// Variant 2: array of column definitions
if (Array.isArray(colsOrDef)) {
if (colsOrDef.length === 0) continue // skip empty tables
const columns = colsOrDef.map((c: any) => ({
name: c.name,
type: c.type ?? c.data_type ?? "VARCHAR",
}))
tables[tableName] = { columns }
} else if (typeof colsOrDef === "object") {
// Variant 3: already has a `columns` array
if (Array.isArray(colsOrDef.columns)) {
if (colsOrDef.columns.length === 0) continue // skip empty tables
tables[tableName] = colsOrDef
} else {
// Variant 1: flat map { "col_name": "TYPE", ... }
const entries = Object.entries(colsOrDef)
if (entries.length === 0) continue // skip empty tables
const columns = entries.map(([colName, colType]) => ({
name: colName,
type: String(colType),
}))
tables[tableName] = { columns }
}
}
}
return { tables }
}

/**
* Normalize a schema_context into SchemaDefinition JSON format.
* Accepts both flat and SchemaDefinition formats.
*/
function normalizeSchemaContext(ctx: Record<string, any>): string {
if (isSchemaDefinitionFormat(ctx)) {
return JSON.stringify(ctx)
}
return JSON.stringify(flatToSchemaDefinition(ctx))
}

/**
* Resolve a Schema from a file path or inline JSON context.
* Returns null when neither source is provided.
Expand All @@ -19,7 +103,7 @@ export function resolveSchema(
return Schema.fromFile(schemaPath)
}
if (schemaContext && Object.keys(schemaContext).length > 0) {
return Schema.fromJson(JSON.stringify(schemaContext))
return Schema.fromJson(normalizeSchemaContext(schemaContext))
}
return null
}
Expand Down
15 changes: 13 additions & 2 deletions packages/opencode/src/altimate/native/sql/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import type {
SchemaDiffResult,
} from "../types"

/** Register all composite SQL handlers with the Dispatcher.
* Exported so tests can re-register after Dispatcher.reset(). */
export function registerAllSql(): void {

// ---------------------------------------------------------------------------
// sql.analyze — lint + semantics + safety
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -362,8 +366,10 @@ register("sql.rewrite", async (params) => {
rewritten_sql: result.suggestions?.[0]?.rewritten_sql ?? null,
rewrites_applied: result.suggestions?.map((s: any) => ({
rule: s.rule,
description: s.explanation,
rewritten_sql: s.rewritten_sql,
original_fragment: params.sql,
rewritten_fragment: s.rewritten_sql ?? params.sql,
explanation: s.explanation ?? s.improvement ?? "",
can_auto_apply: (s.confidence ?? 0) >= 0.7,
})) ?? [],
}
} catch (e) {
Expand Down Expand Up @@ -430,3 +436,8 @@ register("lineage.check", async (params) => {
} satisfies LineageCheckResult
}
})

} // end registerAllSql

// Auto-register on module load
registerAllSql()
21 changes: 16 additions & 5 deletions packages/opencode/src/altimate/tools/altimate-core-classify-pii.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export const AltimateCoreClassifyPiiTool = Tool.define("altimate_core_classify_p
schema_context: args.schema_context,
})
const data = result.data as Record<string, any>
const findingCount = data.findings?.length ?? 0
const piiColumns = data.columns ?? data.findings ?? []
const findingCount = piiColumns.length
return {
title: `PII Classification: ${findingCount} finding(s)`,
metadata: { success: result.success, finding_count: findingCount },
Expand All @@ -31,10 +32,20 @@ export const AltimateCoreClassifyPiiTool = Tool.define("altimate_core_classify_p

function formatClassifyPii(data: Record<string, any>): string {
if (data.error) return `Error: ${data.error}`
if (!data.findings?.length) return "No PII columns detected."
const lines = ["PII columns found:\n"]
for (const f of data.findings) {
lines.push(` ${f.table}.${f.column}: ${f.category} (${f.confidence} confidence)`)
const piiColumns = data.columns ?? data.findings ?? []
if (!piiColumns.length) return "No PII columns detected."
const lines: string[] = []
if (data.risk_level) lines.push(`Risk level: ${data.risk_level}`)
if (data.pii_count != null) lines.push(`PII columns: ${data.pii_count} of ${data.total_columns}`)
lines.push("")
lines.push("PII columns found:")
for (const f of piiColumns) {
const classification = f.classification ?? f.category ?? "PII"
const confidence = f.confidence ?? "high"
const table = f.table ?? "unknown"
const column = f.column ?? "unknown"
lines.push(` ${table}.${column}: ${classification} (${confidence} confidence)`)
if (f.suggested_masking) lines.push(` Masking: ${f.suggested_masking}`)
}
return lines.join("\n")
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,26 @@ export const AltimateCoreColumnLineageTool = Tool.define("altimate_core_column_l

function formatColumnLineage(data: Record<string, any>): string {
if (data.error) return `Error: ${data.error}`
if (!data.column_lineage?.length) return "No column lineage edges found."
const lines = ["Column lineage:\n"]
for (const edge of data.column_lineage) {
lines.push(` ${edge.source} -> ${edge.target}${edge.transform ? ` (${edge.transform})` : ""}`)
if (!data.column_lineage?.length && !data.column_dict) return "No column lineage edges found."
const lines: string[] = []

// column_dict: output columns -> source columns mapping
if (data.column_dict && Object.keys(data.column_dict).length > 0) {
lines.push("Column Mappings:")
for (const [target, sources] of Object.entries(data.column_dict)) {
const srcList = Array.isArray(sources) ? (sources as string[]).join(", ") : JSON.stringify(sources)
lines.push(` ${target} ← ${srcList}`)
}
lines.push("")
}

if (data.column_lineage?.length) {
lines.push("Lineage Edges:")
for (const edge of data.column_lineage) {
const transform = edge.lens_type ?? edge.transform_type ?? edge.transform ?? ""
lines.push(` ${edge.source} → ${edge.target}${transform ? ` (${transform})` : ""}`)
}
}
return lines.join("\n")

return lines.length ? lines.join("\n") : "No column lineage edges found."
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const AltimateCoreCompleteTool = Tool.define("altimate_core_complete", {
schema_context: args.schema_context,
})
const data = result.data as Record<string, any>
const count = data.suggestions?.length ?? 0
const count = data.items?.length ?? data.suggestions?.length ?? 0
return {
title: `Complete: ${count} suggestion(s)`,
metadata: { success: result.success, suggestion_count: count },
Expand All @@ -35,9 +35,10 @@ export const AltimateCoreCompleteTool = Tool.define("altimate_core_complete", {

function formatComplete(data: Record<string, any>): string {
if (data.error) return `Error: ${data.error}`
if (!data.suggestions?.length) return "No completions available."
const items = data.items ?? data.suggestions ?? []
if (!items.length) return "No completions available."
const lines = ["Suggestions:\n"]
for (const s of data.suggestions) {
for (const s of items) {
lines.push(` ${s.label ?? s.text} (${s.kind ?? s.type ?? "unknown"})`)
}
return lines.join("\n")
Expand Down
16 changes: 9 additions & 7 deletions packages/opencode/src/altimate/tools/altimate-core-fix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,23 @@ export const AltimateCoreFixTool = Tool.define("altimate_core_fix", {
function formatFix(data: Record<string, any>): string {
if (data.error) return `Error: ${data.error}`
const lines: string[] = []
if (data.fixed_sql) {
if (data.fixed_sql && data.fixed !== false) {
lines.push("Fixed SQL:")
lines.push(data.fixed_sql)
if (data.changes?.length) {
const fixes = data.fixes_applied ?? data.changes ?? []
if (fixes.length) {
lines.push("\nChanges applied:")
for (const c of data.changes) {
lines.push(` - ${c.description ?? c}`)
for (const c of fixes) {

This comment was marked as outdated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 0bf5bedc8. Changed the condition from data.fixed && data.fixed_sql to data.fixed_sql && data.fixed !== false — so a valid fixed_sql is shown when fixed is either true or absent (undefined). Only an explicit fixed: false suppresses the output.

lines.push(` - ${c.description ?? c.message ?? c}`)
}
}
} else {
lines.push("Could not auto-fix the SQL.")
if (data.errors?.length) {
const unfixable = data.unfixable_errors ?? data.errors ?? []
if (unfixable.length) {
lines.push("\nErrors found:")
for (const e of data.errors) {
lines.push(` - ${e.message ?? e}`)
for (const e of unfixable) {
lines.push(` - ${e.message ?? e.reason ?? e}`)
}
}
}
Expand Down
22 changes: 18 additions & 4 deletions packages/opencode/src/altimate/tools/altimate-core-grade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ export const AltimateCoreGradeTool = Tool.define("altimate_core_grade", {
schema_context: args.schema_context,
})
const data = result.data as Record<string, any>
const grade = data.overall_grade ?? data.grade
const score = data.scores?.overall != null ? Math.round(data.scores.overall * 100) : data.score
return {
title: `Grade: ${data.grade ?? "?"}`,
metadata: { success: result.success, grade: data.grade, score: data.score },
title: `Grade: ${grade ?? "?"}`,
metadata: { success: result.success, grade, score },
output: formatGrade(data),
}
} catch (e) {
Expand All @@ -33,8 +35,20 @@ export const AltimateCoreGradeTool = Tool.define("altimate_core_grade", {
function formatGrade(data: Record<string, any>): string {
if (data.error) return `Error: ${data.error}`
const lines: string[] = []
lines.push(`Grade: ${data.grade}`)
if (data.score != null) lines.push(`Score: ${data.score}/100`)
const grade = data.overall_grade ?? data.grade
lines.push(`Grade: ${grade}`)
const scores = data.scores
if (scores) {
const overall = scores.overall != null ? Math.round(scores.overall * 100) : null
if (overall != null) lines.push(`Score: ${overall}/100`)
lines.push("\nCategory scores:")
if (scores.syntax != null) lines.push(` syntax: ${Math.round(scores.syntax * 100)}/100`)
if (scores.style != null) lines.push(` style: ${Math.round(scores.style * 100)}/100`)
if (scores.safety != null) lines.push(` safety: ${Math.round(scores.safety * 100)}/100`)
if (scores.complexity != null) lines.push(` complexity: ${Math.round(scores.complexity * 100)}/100`)
} else if (data.score != null) {
lines.push(`Score: ${data.score}/100`)
}
if (data.categories) {
lines.push("\nCategory scores:")
for (const [cat, score] of Object.entries(data.categories)) {
Expand Down
17 changes: 15 additions & 2 deletions packages/opencode/src/altimate/tools/altimate-core-prune-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ export const AltimateCorePruneSchemaTool = Tool.define("altimate_core_prune_sche

function formatPruneSchema(data: Record<string, any>): string {
if (data.error) return `Error: ${data.error}`
if (data.pruned) return JSON.stringify(data.pruned, null, 2)
return JSON.stringify(data, null, 2)
const lines: string[] = []
if (data.tables_pruned != null) {
lines.push(`Pruned ${data.tables_pruned} of ${data.total_tables} tables to ${data.relevant_tables?.length ?? "?"} relevant.`)
}
if (data.relevant_tables?.length) {
lines.push(`Relevant tables: ${data.relevant_tables.join(", ")}`)
}
if (data.pruned_schema_yaml) {
lines.push("")
lines.push(data.pruned_schema_yaml)
} else if (data.pruned) {
lines.push("")
lines.push(JSON.stringify(data.pruned, null, 2))
}
return lines.length > 0 ? lines.join("\n") : JSON.stringify(data, null, 2)
}
24 changes: 19 additions & 5 deletions packages/opencode/src/altimate/tools/altimate-core-query-pii.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export const AltimateCoreQueryPiiTool = Tool.define("altimate_core_query_pii", {
schema_context: args.schema_context,
})
const data = result.data as Record<string, any>
const exposureCount = data.exposures?.length ?? 0
const piiCols = data.pii_columns ?? data.exposures ?? []
const exposureCount = piiCols.length
return {
title: `Query PII: ${exposureCount === 0 ? "CLEAN" : `${exposureCount} exposure(s)`}`,
metadata: { success: result.success, exposure_count: exposureCount },
Expand All @@ -33,10 +34,23 @@ export const AltimateCoreQueryPiiTool = Tool.define("altimate_core_query_pii", {

function formatQueryPii(data: Record<string, any>): string {
if (data.error) return `Error: ${data.error}`
if (!data.exposures?.length) return "Query does not access PII columns."
const lines = ["PII exposure detected:\n"]
for (const e of data.exposures) {
lines.push(` ${e.column}: ${e.category} (${e.risk ?? "medium"} risk)`)
const piiCols = data.pii_columns ?? data.exposures ?? []
if (!piiCols.length) return "Query does not access PII columns."
const lines: string[] = []
if (data.risk_level) lines.push(`Risk level: ${data.risk_level}`)
lines.push("PII exposure detected:\n")
for (const e of piiCols) {
const classification = e.classification ?? e.category ?? "PII"
const table = e.table ?? "unknown"
const column = e.column ?? "unknown"
lines.push(` ${table}.${column}: ${classification}`)
if (e.suggested_masking) lines.push(` Masking: ${e.suggested_masking}`)
}
if (data.suggested_alternatives?.length) {
lines.push("\nSuggested alternatives:")
for (const alt of data.suggested_alternatives) {
lines.push(` - ${alt}`)
}
}
return lines.join("\n")
}
Loading
Loading