Skip to content

Commit f87e9ac

Browse files
suryaiyer95claudeanandgupta42
authored
fix: [AI-5975] propagate tool error messages to telemetry (#429)
* fix: [AI-5975] propagate actual error messages to telemetry instead of "unknown error" Root cause: 6,905+ telemetry entries showed "unknown error" because tools did not set `metadata.error` on failure paths. The telemetry layer in `tool.ts` reads `metadata.error` — when missing, it logs "unknown error". Changes: - Add `error` field to `metadata` on all failure paths across 12 failing tools: `altimate_core_validate`, `altimate_core_fix`, `altimate_core_correct`, `altimate_core_semantics`, `altimate_core_equivalence`, `sql_explain`, `sql_analyze`, `finops_query_history`, `finops_expensive_queries`, `finops_analyze_credits`, `finops_unused_resources`, `finops_warehouse_advice` - Add error extraction functions for nested napi-rs response structures: `extractValidationErrors()` (data.errors[]), `extractFixErrors()` (data.unfixable_errors[]), `extractCorrectErrors()` (data.final_validation.errors[]), `extractSemanticsErrors()` (data.validation_errors[]), `extractEquivalenceErrors()` (data.validation_errors[]) - Wire `schema_path`/`schema_context` params through `sql_analyze` tool to dispatcher (were completely disconnected — handler expected them) - Add `schema_path` to `SqlAnalyzeParams` type - Surface `unfixable_errors` from `sql.fix` handler in `register.ts` - Clean up tool descriptions: remove "Rust-based altimate-core engine" boilerplate, add schema guidance where applicable - Add `error?: string` to `Tool.Metadata` interface in `tool.ts` - Add 18 regression tests using mock dispatcher with real failure shapes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] early-return "No schema provided" error for `validate`, `semantics`, `equivalence` Tools that require schema (`altimate_core_validate`, `altimate_core_semantics`, `altimate_core_equivalence`) now return immediately with a clear error when neither `schema_path` nor `schema_context` is provided, instead of calling the napi handler and getting a cryptic "Table ? not found" error. - Handles edge cases: empty string `schema_path` and empty object `schema_context` - Cleaned up dead `noSchema` / `hasSchemaErrors` hint logic in validate - Updated unit + E2E tests for no-schema early return behavior Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] add `.filter(Boolean)` to all error extractors to prevent empty string errors All 5 `extractXxxErrors()` functions could return `""` if error entries had empty message fields, causing `"Error: "` output and breaking `alreadyValid` logic in the fix tool. Added `.filter(Boolean)` to all extractors. Also added regression test for empty string edge case. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] treat "not equivalent" as valid result in `altimate_core_equivalence` The handler returns `success: false` when queries are not equivalent, but "not equivalent" is a valid analysis result — not a failure. This was causing false `core_failure` telemetry events with "unknown error". Uses the same `isRealFailure = !!error` pattern already applied to `sql_analyze` and other tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] address PR review feedback - Guard `result.data ?? {}` to prevent TypeError when dispatcher returns no data - Keep formatted output for LLM context; use `metadata.error` only for telemetry - Filter empty strings and add `e.message` fallback in `register.ts` unfixable error extraction - Fix misleading comment in `sql-analyze.ts` about handler success semantics - Ensure `finops-analyze-credits` always sets concrete error string in metadata - Add `success: false` to `schema-cache-status` catch path metadata - Show "ERROR" title in equivalence when there's a real error (not "DIFFERENT") Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] use error-driven title in semantics, generic error label in analyze - Semantics: show "ERROR" title and pass error to `formatSemantics()` when `result.error` or `validation_errors` are present - Analyze: use generic "ERROR" label instead of "PARSE ERROR" since the error path can carry non-parse failures too Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] ensure `metadata.error` is never `undefined` on success paths - `complete`, `grade`: use conditional spread `...(error && { error })` to avoid setting `metadata.error = undefined` on success - `sql-explain`: remove `error` from success path metadata entirely, add `?? "Unknown error"` fallback to failure path - `finops-expensive-queries`, `finops-query-history`, `finops-unused-resources`, `finops-warehouse-advice`: add `?? "Unknown error"` fallback so metadata always has a concrete error string on failure Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] dispatcher wrappers: `success` means "handler completed", not "result was positive" 12 `altimate_core.*` dispatcher handlers derived `success` from result data (`data.equivalent !== false`, `data.valid !== false`, etc.), conflating "the tool crashed" with "the tool gave a negative finding". Now all handlers return `ok(true, data)` when the Rust NAPI call completes without throwing. Parse failures still throw → `fail(e)` → `success: false` with the actual error message. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] address code review findings for tool wrappers - Add contract documentation to `ok()` explaining `success` semantics - Make `extractEquivalenceErrors` and `extractSemanticsErrors` defensive against object-type `validation_errors` entries - Apply `isRealFailure` pattern to validate tool for consistent error handling Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] revert validate tool to use dispatcher's `result.success` flag The `isRealFailure` pattern from the review fix commit should not have been applied to validate. The dispatcher already returns the correct `success` flag via `ok()`. Validation findings (table not found) are semantic results reported in data fields — the dispatcher's success flag already handles this correctly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] make error propagation tests self-contained with mocks Tests previously depended on the real NAPI binary which isn't available in CI. Replace all real handler calls with dispatcher mocks that return the same response shapes, matching actual tool wrapper behavior: - validate: `result.success` passthrough — validation findings are semantic results, not operational failures - semantics: `result.success` passthrough — `validation_errors` surfaced in `metadata.error` for telemetry but `success` unchanged - equivalence: `isRealFailure` overrides `success` when `validation_errors` exist (existing wrapper logic) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] propagate tool error messages to telemetry across all 25 tools - Add `?? {}` null guard on `result.data` for all altimate-core tool wrappers - Extract `result.error ?? data.error` and spread into metadata conditionally - Add `error: msg` to catch block metadata for impact-analysis, lineage-check - Fix sql-fix unconditional error spread to conditional `...(result.error && { error })` - Add comprehensive test suite (90 tests) covering all error propagation paths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] add missing `dialect` fields to test args for type-check Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] remove spurious `dialect` from grade test (no such param) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] update test expectation to match error title format after merge The test expected "PARSE ERROR" in title but the actual format is "Analyze: ERROR [confidence]". Updated assertion to match. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: [AI-5975] add sql_quality telemetry for issue prevention metrics (#446) * feat: [AI-5975] add `sql_quality` telemetry for issue prevention metrics Add a new `sql_quality` telemetry event that fires whenever tools successfully detect SQL issues — turning findings into measurable "issues prevented" data in App Insights. Architecture: - New `sql_quality` event type in `Telemetry.Event` with `finding_count`, `by_severity`, `by_category`, `has_schema`, `dialect`, `duration_ms` - New `Telemetry.Finding` interface and `aggregateFindings()` helper - Centralized emission in `tool.ts` — checks `metadata.findings` array after any tool completes, aggregates counts, emits event - Tools populate `metadata.findings` with `{category, severity}` pairs: - `sql_analyze`: issue type + severity from lint/semantic/safety analysis - `altimate_core_validate`: classified validation errors (missing_table, missing_column, syntax_error, type_mismatch) - `altimate_core_semantics`: rule/type + severity from semantic checks - `altimate_core_fix`: fix_applied / unfixable_error categories - `altimate_core_correct`: correction_applied findings - `altimate_core_equivalence`: equivalence_difference findings PII-safe: only category names and severity levels flow to telemetry, never SQL content. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: [AI-5975] drop `by_severity` from `sql_quality` telemetry, address PR review - Remove `severity` from `Finding` interface — only `category` matters - Drop `by_severity` from `sql_quality` event type and `aggregateFindings` - Gate `sql_quality` emission on `!isSoftFailure` to avoid double-counting with `core_failure` events (Copilot review feedback) - Simplify semantics tool: use fixed `"semantic_issue"` category instead of dead `issue.rule ?? issue.type` fallback chain (CodeRabbit review feedback) - Update test header to accurately describe what tests cover - Fix sql_analyze test to use honest coarse categories (`"lint"`, `"safety"`) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: [AI-5975] drop `by_severity` from `sql_quality` telemetry, address PR review - Remove `severity` from `Finding` interface — only `category` matters - Drop `by_severity` from `sql_quality` event type and `aggregateFindings` - Gate `sql_quality` emission on `!isSoftFailure` to avoid double-counting with `core_failure` events (Copilot review feedback) - Simplify semantics tool: use fixed `"semantic_issue"` category instead of dead `issue.rule ?? issue.type` fallback chain (CodeRabbit review feedback) - Update test header to accurately describe what tests cover - Fix sql_analyze test to use honest coarse categories (`"lint"`, `"safety"`) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] decouple `metadata.success` from domain outcomes in finding tools Five tools were suppressing `sql_quality` telemetry because their `metadata.success` tracked domain outcomes (SQL invalid, policy violated, queries not equivalent) rather than engine execution success. `tool.ts` gate: `!isSoftFailure && findings.length > 0` - `isSoftFailure = metadata.success === false` - Tools that found issues had `success: false` → findings suppressed Fix: set `success: true` when the engine ran (even if it found problems). Domain outcomes remain in dedicated fields (`valid`, `pass`, `equivalent`, `fixed`). Only catch blocks set `success: false` (real engine crashes). Affected tools: - `altimate_core_validate` — validation errors now emit `sql_quality` - `altimate_core_semantics` — semantic issues now emit `sql_quality` - `altimate_core_policy` — policy violations now emit `sql_quality` - `altimate_core_equivalence` — differences now emit `sql_quality` - `altimate_core_fix` — unfixable errors now emit `sql_quality` Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: [AI-5975] remove hardcoded `dialect: "snowflake"` from core tools - Remove `dialect` from metadata in 8 altimate-core/impact tools that don't accept a dialect parameter (it was always hardcoded to "snowflake") - Make `dialect` optional in `sql_quality` telemetry event type - Only emit `dialect` when the tool actually provides it (sql-analyze, sql-optimize, schema-diff still do via `args.dialect`) - Tracked as #455 for adding proper dialect parameter support later Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: [AI-5975] guard finding arrays with `Array.isArray` for defensive safety If a dispatcher returns a non-array for `errors`, `violations`, `issues`, or `changes`, the `?? []` fallback handles null/undefined but not other types. `Array.isArray` prevents `.map()` from throwing on unexpected payloads. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: add missing `altimate_change` markers and prettier formatting in `tool.ts` * style: prettier formatting on all changed tool files * fix: preserve original formatting of `errorMsg` ternary to avoid marker guard false positive * fix: restore original `errorMsg` indentation to match `main` * fix: correct indentation of `altimate_change end` marker in `tool.ts` * fix: propagate `metadata.error` in `sql-optimize` and `sql-translate`, compute `has_schema` in `sql-analyze` * fix: remove unused `@ts-expect-error` directives now that `define()` generic is simplified --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: anandgupta42 <93243293+anandgupta42@users.noreply.github.com> Co-authored-by: anandgupta42 <anand@altimate.ai>
1 parent 722153d commit f87e9ac

File tree

62 files changed

+3463
-1099
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+3463
-1099
lines changed

packages/opencode/src/altimate/native/altimate-core.ts

Lines changed: 417 additions & 438 deletions
Large diffs are not rendered by default.

packages/opencode/src/altimate/native/sql/register.ts

Lines changed: 428 additions & 391 deletions
Large diffs are not rendered by default.

packages/opencode/src/altimate/native/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ export interface SqlExecuteResult {
2424
export interface SqlAnalyzeParams {
2525
sql: string
2626
dialect?: string
27+
schema_path?: string
2728
schema_context?: Record<string, any>
2829
}
2930

3031
export interface SqlAnalyzeIssue {
3132
type: string
33+
rule?: string
3234
severity: string
3335
message: string
3436
recommendation: string
@@ -385,6 +387,7 @@ export interface SqlFixResult {
385387
error_message: string
386388
suggestions: SqlFixSuggestion[]
387389
suggestion_count: number
390+
error?: string
388391
}
389392

390393
// --- SQL Autocomplete ---

packages/opencode/src/altimate/telemetry/index.ts

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ export namespace Telemetry {
4848
// No nested objects: Azure App Insights custom measures must be top-level numbers.
4949
tokens_input: number
5050
tokens_output: number
51-
tokens_reasoning?: number // only for reasoning models
52-
tokens_cache_read?: number // only when a cached prompt was reused
51+
tokens_reasoning?: number // only for reasoning models
52+
tokens_cache_read?: number // only when a cached prompt was reused
5353
tokens_cache_write?: number // only when a new cache entry was written
5454
}
5555
| {
@@ -401,19 +401,27 @@ export namespace Telemetry {
401401
session_id: string
402402
tool_name: string
403403
tool_category: string
404-
error_class:
405-
| "parse_error"
406-
| "connection"
407-
| "timeout"
408-
| "validation"
409-
| "internal"
410-
| "permission"
411-
| "unknown"
404+
error_class: "parse_error" | "connection" | "timeout" | "validation" | "internal" | "permission" | "unknown"
412405
error_message: string
413406
input_signature: string
414407
masked_args?: string
415408
duration_ms: number
416409
}
410+
// altimate_change start — sql quality telemetry for issue prevention metrics
411+
| {
412+
type: "sql_quality"
413+
timestamp: number
414+
session_id: string
415+
tool_name: string
416+
tool_category: string
417+
finding_count: number
418+
/** JSON-encoded Record<string, number> — count per issue category */
419+
by_category: string
420+
has_schema: boolean
421+
dialect?: string
422+
duration_ms: number
423+
}
424+
// altimate_change end
417425

418426
const ERROR_PATTERNS: Array<{
419427
class: Telemetry.Event & { type: "core_failure" } extends { error_class: infer C } ? C : never
@@ -476,20 +484,40 @@ export namespace Telemetry {
476484

477485
// Mirrors altimate-sdk (Rust) SENSITIVE_KEYS — keep in sync.
478486
const SENSITIVE_KEYS: string[] = [
479-
"key", "api_key", "apikey", "apiKey", "token", "access_token", "refresh_token",
480-
"secret", "secret_key", "password", "passwd", "pwd",
481-
"credential", "credentials", "authorization", "auth",
482-
"signature", "sig", "private_key", "connection_string",
487+
"key",
488+
"api_key",
489+
"apikey",
490+
"apiKey",
491+
"token",
492+
"access_token",
493+
"refresh_token",
494+
"secret",
495+
"secret_key",
496+
"password",
497+
"passwd",
498+
"pwd",
499+
"credential",
500+
"credentials",
501+
"authorization",
502+
"auth",
503+
"signature",
504+
"sig",
505+
"private_key",
506+
"connection_string",
483507
// camelCase variants not caught by prefix/suffix matching
484-
"authtoken", "accesstoken", "refreshtoken", "bearertoken", "jwttoken",
485-
"jwtsecret", "clientsecret", "appsecret",
508+
"authtoken",
509+
"accesstoken",
510+
"refreshtoken",
511+
"bearertoken",
512+
"jwttoken",
513+
"jwtsecret",
514+
"clientsecret",
515+
"appsecret",
486516
]
487517

488518
function isSensitiveKey(key: string): boolean {
489519
const lower = key.toLowerCase()
490-
return SENSITIVE_KEYS.some(
491-
(k) => lower === k || lower.endsWith(`_${k}`) || lower.startsWith(`${k}_`),
492-
)
520+
return SENSITIVE_KEYS.some((k) => lower === k || lower.endsWith(`_${k}`) || lower.startsWith(`${k}_`))
493521
}
494522

495523
export function maskString(s: string): string {
@@ -674,7 +702,7 @@ export namespace Telemetry {
674702
// before Instance.provide()). Treat config failures as "not disabled" —
675703
// the env var check above is the early-init escape hatch.
676704
try {
677-
const userConfig = await Config.get() as any
705+
const userConfig = (await Config.get()) as any
678706
if (userConfig.telemetry?.disabled) {
679707
buffer = []
680708
return
@@ -789,6 +817,22 @@ export namespace Telemetry {
789817
}
790818
}
791819

820+
// altimate_change start — sql quality telemetry types
821+
/** Lightweight finding record for quality telemetry. Only category — never SQL content. */
822+
export interface Finding {
823+
category: string
824+
}
825+
826+
/** Aggregate an array of findings into category counts suitable for the sql_quality event. */
827+
export function aggregateFindings(findings: Finding[]): Record<string, number> {
828+
const by_category: Record<string, number> = {}
829+
for (const f of findings) {
830+
by_category[f.category] = (by_category[f.category] ?? 0) + 1
831+
}
832+
return by_category
833+
}
834+
// altimate_change end
835+
792836
export async function shutdown() {
793837
// Wait for init to complete so we know whether telemetry is enabled
794838
// and have a valid endpoint to flush to. init() is fire-and-forget

packages/opencode/src/altimate/tools/altimate-core-check.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,58 @@
11
import z from "zod"
22
import { Tool } from "../../tool/tool"
33
import { Dispatcher } from "../native"
4+
import type { Telemetry } from "../telemetry"
45

56
export const AltimateCoreCheckTool = Tool.define("altimate_core_check", {
67
description:
7-
"Run full analysis pipeline: validate + lint + safety scan + PII check using the Rust-based altimate-core engine. Single call for comprehensive SQL analysis.",
8+
"Run full analysis pipeline: validate + lint + safety scan + PII check. Single call for comprehensive SQL analysis. Provide schema_context or schema_path for accurate table/column resolution.",
89
parameters: z.object({
910
sql: z.string().describe("SQL query to analyze"),
1011
schema_path: z.string().optional().describe("Path to YAML/JSON schema file"),
1112
schema_context: z.record(z.string(), z.any()).optional().describe("Inline schema definition"),
1213
}),
1314
async execute(args, ctx) {
15+
const hasSchema = !!(args.schema_path || (args.schema_context && Object.keys(args.schema_context).length > 0))
1416
try {
1517
const result = await Dispatcher.call("altimate_core.check", {
1618
sql: args.sql,
1719
schema_path: args.schema_path ?? "",
1820
schema_context: args.schema_context,
1921
})
20-
const data = result.data as Record<string, any>
22+
const data = (result.data ?? {}) as Record<string, any>
23+
const error = result.error ?? data.error
24+
// altimate_change start — sql quality findings for telemetry
25+
const findings: Telemetry.Finding[] = []
26+
for (const err of data.validation?.errors ?? []) {
27+
findings.push({ category: "validation_error" })
28+
}
29+
for (const f of data.lint?.findings ?? []) {
30+
findings.push({ category: f.rule ?? "lint" })
31+
}
32+
for (const t of data.safety?.threats ?? []) {
33+
findings.push({ category: t.type ?? "safety_threat" })
34+
}
35+
for (const p of data.pii?.findings ?? []) {
36+
findings.push({ category: "pii_detected" })
37+
}
38+
// altimate_change end
2139
return {
2240
title: `Check: ${formatCheckTitle(data)}`,
23-
metadata: { success: result.success },
41+
metadata: {
42+
success: result.success,
43+
has_schema: hasSchema,
44+
...(error && { error }),
45+
...(findings.length > 0 && { findings }),
46+
},
2447
output: formatCheck(data),
2548
}
2649
} catch (e) {
2750
const msg = e instanceof Error ? e.message : String(e)
28-
return { title: "Check: ERROR", metadata: { success: false }, output: `Failed: ${msg}` }
51+
return {
52+
title: "Check: ERROR",
53+
metadata: { success: false, has_schema: hasSchema, error: msg },
54+
output: `Failed: ${msg}`,
55+
}
2956
}
3057
},
3158
})

packages/opencode/src/altimate/tools/altimate-core-classify-pii.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Dispatcher } from "../native"
44

55
export const AltimateCoreClassifyPiiTool = Tool.define("altimate_core_classify_pii", {
66
description:
7-
"Classify PII columns in a schema using the Rust-based altimate-core engine. Identifies columns likely containing personal identifiable information by name patterns and data types.",
7+
"Classify PII columns in a schema. Identifies columns likely containing personal identifiable information by name patterns and data types. Provide schema_context or schema_path for accurate table/column resolution.",
88
parameters: z.object({
99
schema_path: z.string().optional().describe("Path to YAML/JSON schema file"),
1010
schema_context: z.record(z.string(), z.any()).optional().describe("Inline schema definition"),
@@ -15,17 +15,22 @@ export const AltimateCoreClassifyPiiTool = Tool.define("altimate_core_classify_p
1515
schema_path: args.schema_path ?? "",
1616
schema_context: args.schema_context,
1717
})
18-
const data = result.data as Record<string, any>
18+
const data = (result.data ?? {}) as Record<string, any>
1919
const piiColumns = data.columns ?? data.findings ?? []
2020
const findingCount = piiColumns.length
21+
const error = result.error ?? data.error
2122
return {
2223
title: `PII Classification: ${findingCount} finding(s)`,
23-
metadata: { success: result.success, finding_count: findingCount },
24+
metadata: { success: result.success, finding_count: findingCount, ...(error && { error }) },
2425
output: formatClassifyPii(data),
2526
}
2627
} catch (e) {
2728
const msg = e instanceof Error ? e.message : String(e)
28-
return { title: "PII Classification: ERROR", metadata: { success: false, finding_count: 0 }, output: `Failed: ${msg}` }
29+
return {
30+
title: "PII Classification: ERROR",
31+
metadata: { success: false, finding_count: 0, error: msg },
32+
output: `Failed: ${msg}`,
33+
}
2934
}
3035
},
3136
})

packages/opencode/src/altimate/tools/altimate-core-column-lineage.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Dispatcher } from "../native"
44

55
export const AltimateCoreColumnLineageTool = Tool.define("altimate_core_column_lineage", {
66
description:
7-
"Trace schema-aware column lineage using the Rust-based altimate-core engine. Maps how columns flow through a query from source tables to output. Requires altimate_core.init() with API key.",
7+
"Trace schema-aware column lineage. Maps how columns flow through a query from source tables to output. Requires altimate_core.init() with API key. Provide schema_context or schema_path for accurate table/column resolution.",
88
parameters: z.object({
99
sql: z.string().describe("SQL query to trace lineage for"),
1010
dialect: z.string().optional().describe("SQL dialect (e.g. snowflake, bigquery)"),
@@ -19,16 +19,21 @@ export const AltimateCoreColumnLineageTool = Tool.define("altimate_core_column_l
1919
schema_path: args.schema_path ?? "",
2020
schema_context: args.schema_context,
2121
})
22-
const data = result.data as Record<string, any>
22+
const data = (result.data ?? {}) as Record<string, any>
2323
const edgeCount = data.column_lineage?.length ?? 0
24+
const error = result.error ?? data.error
2425
return {
2526
title: `Column Lineage: ${edgeCount} edge(s)`,
26-
metadata: { success: result.success, edge_count: edgeCount },
27+
metadata: { success: result.success, edge_count: edgeCount, ...(error && { error }) },
2728
output: formatColumnLineage(data),
2829
}
2930
} catch (e) {
3031
const msg = e instanceof Error ? e.message : String(e)
31-
return { title: "Column Lineage: ERROR", metadata: { success: false, edge_count: 0 }, output: `Failed: ${msg}` }
32+
return {
33+
title: "Column Lineage: ERROR",
34+
metadata: { success: false, edge_count: 0, error: msg },
35+
output: `Failed: ${msg}`,
36+
}
3237
}
3338
},
3439
})

packages/opencode/src/altimate/tools/altimate-core-compare.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Dispatcher } from "../native"
44

55
export const AltimateCoreCompareTool = Tool.define("altimate_core_compare", {
66
description:
7-
"Structurally compare two SQL queries using the Rust-based altimate-core engine. Identifies differences in table references, join conditions, filters, projections, and aggregations.",
7+
"Structurally compare two SQL queries. Identifies differences in table references, join conditions, filters, projections, and aggregations.",
88
parameters: z.object({
99
left_sql: z.string().describe("First SQL query"),
1010
right_sql: z.string().describe("Second SQL query"),
@@ -17,16 +17,21 @@ export const AltimateCoreCompareTool = Tool.define("altimate_core_compare", {
1717
right_sql: args.right_sql,
1818
dialect: args.dialect ?? "",
1919
})
20-
const data = result.data as Record<string, any>
20+
const data = (result.data ?? {}) as Record<string, any>
2121
const diffCount = data.differences?.length ?? 0
22+
const error = result.error ?? data.error
2223
return {
2324
title: `Compare: ${diffCount === 0 ? "IDENTICAL" : `${diffCount} difference(s)`}`,
24-
metadata: { success: result.success, difference_count: diffCount },
25+
metadata: { success: result.success, difference_count: diffCount, ...(error && { error }) },
2526
output: formatCompare(data),
2627
}
2728
} catch (e) {
2829
const msg = e instanceof Error ? e.message : String(e)
29-
return { title: "Compare: ERROR", metadata: { success: false, difference_count: 0 }, output: `Failed: ${msg}` }
30+
return {
31+
title: "Compare: ERROR",
32+
metadata: { success: false, difference_count: 0, error: msg },
33+
output: `Failed: ${msg}`,
34+
}
3035
}
3136
},
3237
})

packages/opencode/src/altimate/tools/altimate-core-complete.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Dispatcher } from "../native"
44

55
export const AltimateCoreCompleteTool = Tool.define("altimate_core_complete", {
66
description:
7-
"Get cursor-aware SQL completion suggestions using the Rust-based altimate-core engine. Returns table names, column names, functions, and keywords relevant to the cursor position.",
7+
"Get cursor-aware SQL completion suggestions. Returns table names, column names, functions, and keywords relevant to the cursor position. Provide schema_context or schema_path for accurate table/column resolution.",
88
parameters: z.object({
99
sql: z.string().describe("Partial SQL query"),
1010
cursor_pos: z.number().describe("Cursor position (0-indexed character offset)"),
@@ -19,16 +19,21 @@ export const AltimateCoreCompleteTool = Tool.define("altimate_core_complete", {
1919
schema_path: args.schema_path ?? "",
2020
schema_context: args.schema_context,
2121
})
22-
const data = result.data as Record<string, any>
22+
const data = (result.data ?? {}) as Record<string, any>
2323
const count = data.items?.length ?? data.suggestions?.length ?? 0
24+
const error = result.error ?? (data as any).error
2425
return {
2526
title: `Complete: ${count} suggestion(s)`,
26-
metadata: { success: result.success, suggestion_count: count },
27+
metadata: { success: result.success, suggestion_count: count, ...(error && { error }) },
2728
output: formatComplete(data),
2829
}
2930
} catch (e) {
3031
const msg = e instanceof Error ? e.message : String(e)
31-
return { title: "Complete: ERROR", metadata: { success: false, suggestion_count: 0 }, output: `Failed: ${msg}` }
32+
return {
33+
title: "Complete: ERROR",
34+
metadata: { success: false, suggestion_count: 0, error: msg },
35+
output: `Failed: ${msg}`,
36+
}
3237
}
3338
},
3439
})

0 commit comments

Comments
 (0)