@duckcodeailabs/dql-agent 1.4.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 (50) hide show
  1. package/LICENSE +123 -0
  2. package/dist/answer-loop.d.ts +74 -0
  3. package/dist/answer-loop.d.ts.map +1 -0
  4. package/dist/answer-loop.js +170 -0
  5. package/dist/answer-loop.js.map +1 -0
  6. package/dist/index.d.ts +51 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +87 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/kg/build.d.ts +17 -0
  11. package/dist/kg/build.d.ts.map +1 -0
  12. package/dist/kg/build.js +126 -0
  13. package/dist/kg/build.js.map +1 -0
  14. package/dist/kg/sqlite-fts.d.ts +39 -0
  15. package/dist/kg/sqlite-fts.d.ts.map +1 -0
  16. package/dist/kg/sqlite-fts.js +257 -0
  17. package/dist/kg/sqlite-fts.js.map +1 -0
  18. package/dist/kg/types.d.ts +64 -0
  19. package/dist/kg/types.d.ts.map +1 -0
  20. package/dist/kg/types.js +10 -0
  21. package/dist/kg/types.js.map +1 -0
  22. package/dist/providers/claude.d.ts +21 -0
  23. package/dist/providers/claude.d.ts.map +1 -0
  24. package/dist/providers/claude.js +56 -0
  25. package/dist/providers/claude.js.map +1 -0
  26. package/dist/providers/gemini.d.ts +18 -0
  27. package/dist/providers/gemini.d.ts.map +1 -0
  28. package/dist/providers/gemini.js +60 -0
  29. package/dist/providers/gemini.js.map +1 -0
  30. package/dist/providers/index.d.ts +24 -0
  31. package/dist/providers/index.d.ts.map +1 -0
  32. package/dist/providers/index.js +43 -0
  33. package/dist/providers/index.js.map +1 -0
  34. package/dist/providers/ollama.d.ts +21 -0
  35. package/dist/providers/ollama.d.ts.map +1 -0
  36. package/dist/providers/ollama.js +49 -0
  37. package/dist/providers/ollama.js.map +1 -0
  38. package/dist/providers/openai.d.ts +19 -0
  39. package/dist/providers/openai.d.ts.map +1 -0
  40. package/dist/providers/openai.js +44 -0
  41. package/dist/providers/openai.js.map +1 -0
  42. package/dist/providers/types.d.ts +39 -0
  43. package/dist/providers/types.d.ts.map +1 -0
  44. package/dist/providers/types.js +15 -0
  45. package/dist/providers/types.js.map +1 -0
  46. package/dist/skills/loader.d.ts +49 -0
  47. package/dist/skills/loader.d.ts.map +1 -0
  48. package/dist/skills/loader.js +177 -0
  49. package/dist/skills/loader.js.map +1 -0
  50. package/package.json +46 -0
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Build the KG node + edge arrays from a compiled DQL manifest.
3
+ *
4
+ * Inputs:
5
+ * - dql-manifest.json (blocks, dashboards, apps, metrics, dimensions, sources)
6
+ * - dbt manifest (already merged into the DQL manifest's dbtImport.dbtDag)
7
+ * - project Skills folder (loaded separately by the Skills loader)
8
+ *
9
+ * The output is intentionally flat — caller passes it to `KGStore.rebuild()`.
10
+ */
11
+ export function buildKGFromManifest(manifest) {
12
+ const nodes = [];
13
+ const edges = [];
14
+ // Blocks
15
+ for (const block of Object.values(manifest.blocks)) {
16
+ const nodeId = `block:${block.name}`;
17
+ nodes.push({
18
+ nodeId,
19
+ kind: 'block',
20
+ name: block.name,
21
+ domain: block.domain,
22
+ status: block.status,
23
+ owner: block.owner,
24
+ description: block.description,
25
+ tags: block.tags ?? [],
26
+ llmContext: block.llmContext,
27
+ examples: block.examples,
28
+ sourcePath: block.filePath,
29
+ });
30
+ }
31
+ // Metrics
32
+ for (const m of Object.values(manifest.metrics)) {
33
+ const nodeId = `metric:${m.name}`;
34
+ nodes.push({
35
+ nodeId,
36
+ kind: 'metric',
37
+ name: m.name,
38
+ domain: m.domain,
39
+ description: m.description,
40
+ sourcePath: m.filePath,
41
+ });
42
+ }
43
+ // Dimensions
44
+ for (const d of Object.values(manifest.dimensions)) {
45
+ const nodeId = `dimension:${d.name}`;
46
+ nodes.push({
47
+ nodeId,
48
+ kind: 'dimension',
49
+ name: d.name,
50
+ description: d.description,
51
+ sourcePath: d.filePath,
52
+ });
53
+ }
54
+ // Sources / dbt models
55
+ for (const s of Object.values(manifest.sources)) {
56
+ const isDbt = s.origin === 'dbt';
57
+ const kind = isDbt ? 'dbt_model' : 'dbt_source';
58
+ const nodeId = `${kind}:${s.name}`;
59
+ nodes.push({
60
+ nodeId,
61
+ kind,
62
+ name: s.name,
63
+ description: s.dbtModel?.description,
64
+ });
65
+ }
66
+ // Dashboards
67
+ for (const d of Object.values(manifest.dashboards ?? {})) {
68
+ const nodeId = `dashboard:${d.id}`;
69
+ nodes.push({
70
+ nodeId,
71
+ kind: 'dashboard',
72
+ name: d.title,
73
+ domain: d.domain,
74
+ description: d.description,
75
+ tags: d.tags ?? [],
76
+ sourcePath: d.filePath,
77
+ });
78
+ for (const blockId of d.blockIds) {
79
+ edges.push({ src: `block:${blockId}`, dst: nodeId, kind: 'contains' });
80
+ }
81
+ for (const blockName of d.blockPathRefs) {
82
+ edges.push({ src: `block:${blockName}`, dst: nodeId, kind: 'contains' });
83
+ }
84
+ }
85
+ // Apps
86
+ for (const a of Object.values(manifest.apps ?? {})) {
87
+ const nodeId = `app:${a.id}`;
88
+ nodes.push({
89
+ nodeId,
90
+ kind: 'app',
91
+ name: a.name,
92
+ domain: a.domain,
93
+ owner: a.owners[0],
94
+ description: a.description,
95
+ tags: a.tags ?? [],
96
+ sourcePath: a.filePath,
97
+ });
98
+ for (const dashboardId of a.dashboards) {
99
+ edges.push({ src: `dashboard:${dashboardId}`, dst: nodeId, kind: 'contains' });
100
+ }
101
+ }
102
+ // Domains: derive a node per distinct domain seen across blocks/dashboards/apps.
103
+ const domains = new Set();
104
+ for (const block of Object.values(manifest.blocks))
105
+ if (block.domain)
106
+ domains.add(block.domain);
107
+ for (const d of Object.values(manifest.dashboards ?? {}))
108
+ if (d.domain)
109
+ domains.add(d.domain);
110
+ for (const a of Object.values(manifest.apps ?? {}))
111
+ if (a.domain)
112
+ domains.add(a.domain);
113
+ for (const m of Object.values(manifest.metrics))
114
+ if (m.domain)
115
+ domains.add(m.domain);
116
+ for (const d of domains) {
117
+ nodes.push({
118
+ nodeId: `domain:${d}`,
119
+ kind: 'domain',
120
+ name: d,
121
+ domain: d,
122
+ });
123
+ }
124
+ return { nodes, edges };
125
+ }
126
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/kg/build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * SQLite + FTS5 backing store for the knowledge graph.
3
+ *
4
+ * Schema (created on demand):
5
+ * kg_nodes — structured columns + JSON blobs for tags/examples
6
+ * kg_nodes_fts — virtual FTS5 table over name/description/llm_context/tags
7
+ * kg_edges — adjacency list (src, dst, kind)
8
+ * kg_feedback — feedback events for self-learning ranking
9
+ * kg_meta — last-build timestamp + manifest fingerprint
10
+ */
11
+ import type { KGNode, KGEdge, KGSearchHit, KGSearchOptions, KGFeedbackRow } from './types.js';
12
+ export declare class KGStore {
13
+ private db;
14
+ constructor(dbPath: string);
15
+ private initSchema;
16
+ /**
17
+ * Wholesale rebuild — clears every node/edge then inserts the supplied
18
+ * arrays inside one transaction. Feedback rows are preserved across
19
+ * rebuilds because they're index inputs.
20
+ */
21
+ rebuild(nodes: KGNode[], edges: KGEdge[]): void;
22
+ search(options: KGSearchOptions): KGSearchHit[];
23
+ getNode(nodeId: string): KGNode | null;
24
+ getNodesByKind(kind: string, limit?: number): KGNode[];
25
+ recordFeedback(row: KGFeedbackRow): void;
26
+ blockFeedbackScore(blockId: string): {
27
+ up: number;
28
+ down: number;
29
+ };
30
+ /** Surface high-confidence uncertified answers ready for promotion. */
31
+ promotionCandidates(minUps?: number): Array<{
32
+ question: string;
33
+ blockId: string;
34
+ ups: number;
35
+ }>;
36
+ meta(key: string): string | null;
37
+ close(): void;
38
+ }
39
+ //# sourceMappingURL=sqlite-fts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-fts.d.ts","sourceRoot":"","sources":["../../src/kg/sqlite-fts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EACV,MAAM,EACN,MAAM,EACN,WAAW,EACX,eAAe,EACf,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,qBAAa,OAAO;IAClB,OAAO,CAAC,EAAE,CAAoB;gBAElB,MAAM,EAAE,MAAM;IAQ1B,OAAO,CAAC,UAAU;IA8DlB;;;;OAIG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IA+C/C,MAAM,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,EAAE;IA2C/C,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAUtC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,MAAM,EAAE;IAYnD,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IAUxC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;IAajE,uEAAuE;IACvE,mBAAmB,CAAC,MAAM,SAAI,GAAG,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAmB1F,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAMhC,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,257 @@
1
+ /**
2
+ * SQLite + FTS5 backing store for the knowledge graph.
3
+ *
4
+ * Schema (created on demand):
5
+ * kg_nodes — structured columns + JSON blobs for tags/examples
6
+ * kg_nodes_fts — virtual FTS5 table over name/description/llm_context/tags
7
+ * kg_edges — adjacency list (src, dst, kind)
8
+ * kg_feedback — feedback events for self-learning ranking
9
+ * kg_meta — last-build timestamp + manifest fingerprint
10
+ */
11
+ import Database from 'better-sqlite3';
12
+ import { dirname } from 'node:path';
13
+ import { mkdirSync } from 'node:fs';
14
+ export class KGStore {
15
+ db;
16
+ constructor(dbPath) {
17
+ mkdirSync(dirname(dbPath), { recursive: true });
18
+ this.db = new Database(dbPath);
19
+ this.db.pragma('journal_mode = WAL');
20
+ this.db.pragma('foreign_keys = ON');
21
+ this.initSchema();
22
+ }
23
+ initSchema() {
24
+ this.db.exec(`
25
+ CREATE TABLE IF NOT EXISTS kg_nodes (
26
+ node_id TEXT PRIMARY KEY,
27
+ kind TEXT NOT NULL,
28
+ name TEXT NOT NULL,
29
+ domain TEXT,
30
+ status TEXT,
31
+ owner TEXT,
32
+ description TEXT,
33
+ llm_context TEXT,
34
+ tags_json TEXT NOT NULL DEFAULT '[]',
35
+ examples_json TEXT NOT NULL DEFAULT '[]',
36
+ source_path TEXT,
37
+ git_sha TEXT NOT NULL DEFAULT '',
38
+ updated_at TEXT NOT NULL
39
+ );
40
+
41
+ CREATE INDEX IF NOT EXISTS idx_kg_nodes_kind ON kg_nodes(kind);
42
+ CREATE INDEX IF NOT EXISTS idx_kg_nodes_domain ON kg_nodes(domain);
43
+
44
+ CREATE VIRTUAL TABLE IF NOT EXISTS kg_nodes_fts USING fts5(
45
+ node_id UNINDEXED,
46
+ name,
47
+ description,
48
+ llm_context,
49
+ tags,
50
+ kind,
51
+ domain,
52
+ tokenize = 'porter unicode61'
53
+ );
54
+
55
+ CREATE TABLE IF NOT EXISTS kg_edges (
56
+ src TEXT NOT NULL,
57
+ dst TEXT NOT NULL,
58
+ kind TEXT NOT NULL,
59
+ weight REAL NOT NULL DEFAULT 1.0,
60
+ PRIMARY KEY (src, dst, kind)
61
+ );
62
+ CREATE INDEX IF NOT EXISTS idx_kg_edges_src ON kg_edges(src);
63
+ CREATE INDEX IF NOT EXISTS idx_kg_edges_dst ON kg_edges(dst);
64
+
65
+ CREATE TABLE IF NOT EXISTS kg_feedback (
66
+ id TEXT PRIMARY KEY,
67
+ ts TEXT NOT NULL,
68
+ user TEXT NOT NULL,
69
+ question TEXT NOT NULL,
70
+ answer_kind TEXT NOT NULL,
71
+ block_id TEXT,
72
+ rating TEXT NOT NULL CHECK (rating IN ('up', 'down')),
73
+ comment TEXT
74
+ );
75
+ CREATE INDEX IF NOT EXISTS idx_kg_feedback_user ON kg_feedback(user);
76
+ CREATE INDEX IF NOT EXISTS idx_kg_feedback_block ON kg_feedback(block_id);
77
+
78
+ CREATE TABLE IF NOT EXISTS kg_meta (
79
+ key TEXT PRIMARY KEY,
80
+ value TEXT NOT NULL
81
+ );
82
+ `);
83
+ }
84
+ /**
85
+ * Wholesale rebuild — clears every node/edge then inserts the supplied
86
+ * arrays inside one transaction. Feedback rows are preserved across
87
+ * rebuilds because they're index inputs.
88
+ */
89
+ rebuild(nodes, edges) {
90
+ const insertNode = this.db.prepare(`
91
+ INSERT INTO kg_nodes (
92
+ node_id, kind, name, domain, status, owner, description, llm_context,
93
+ tags_json, examples_json, source_path, git_sha, updated_at
94
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
95
+ `);
96
+ const insertFts = this.db.prepare(`
97
+ INSERT INTO kg_nodes_fts (node_id, name, description, llm_context, tags, kind, domain)
98
+ VALUES (?, ?, ?, ?, ?, ?, ?)
99
+ `);
100
+ const insertEdge = this.db.prepare(`
101
+ INSERT OR IGNORE INTO kg_edges (src, dst, kind, weight) VALUES (?, ?, ?, ?)
102
+ `);
103
+ const now = new Date().toISOString();
104
+ const txn = this.db.transaction(() => {
105
+ this.db.prepare('DELETE FROM kg_edges').run();
106
+ this.db.prepare('DELETE FROM kg_nodes_fts').run();
107
+ this.db.prepare('DELETE FROM kg_nodes').run();
108
+ for (const n of nodes) {
109
+ insertNode.run(n.nodeId, n.kind, n.name, n.domain ?? null, n.status ?? null, n.owner ?? null, n.description ?? null, n.llmContext ?? null, JSON.stringify(n.tags ?? []), JSON.stringify(n.examples ?? []), n.sourcePath ?? null, n.gitSha ?? '', now);
110
+ insertFts.run(n.nodeId, n.name, n.description ?? '', n.llmContext ?? '', (n.tags ?? []).join(' '), n.kind, n.domain ?? '');
111
+ }
112
+ for (const e of edges) {
113
+ insertEdge.run(e.src, e.dst, e.kind, e.weight ?? 1.0);
114
+ }
115
+ this.db.prepare(`INSERT OR REPLACE INTO kg_meta (key, value) VALUES ('built_at', ?)`).run(now);
116
+ });
117
+ txn();
118
+ }
119
+ search(options) {
120
+ const { query, kinds, domain, limit = 20 } = options;
121
+ if (!query.trim())
122
+ return [];
123
+ const sanitized = sanitizeFtsQuery(query);
124
+ if (!sanitized)
125
+ return [];
126
+ const filters = [];
127
+ const params = [sanitized];
128
+ if (kinds && kinds.length > 0) {
129
+ filters.push(`f.kind IN (${kinds.map(() => '?').join(', ')})`);
130
+ params.push(...kinds);
131
+ }
132
+ if (domain) {
133
+ filters.push(`f.domain = ?`);
134
+ params.push(domain);
135
+ }
136
+ const whereExtra = filters.length > 0 ? ` AND ${filters.join(' AND ')}` : '';
137
+ const rows = this.db.prepare(`
138
+ SELECT n.*,
139
+ bm25(kg_nodes_fts) AS rank,
140
+ snippet(kg_nodes_fts, -1, '<mark>', '</mark>', '…', 12) AS snip
141
+ FROM kg_nodes_fts AS f
142
+ JOIN kg_nodes AS n ON n.node_id = f.node_id
143
+ WHERE kg_nodes_fts MATCH ?${whereExtra}
144
+ ORDER BY rank
145
+ LIMIT ?
146
+ `).all(...params, limit);
147
+ return rows.map((r) => ({
148
+ node: rowToNode(r),
149
+ score: r.rank ? 1 / (1 + Math.max(0, r.rank)) : 0,
150
+ snippet: r.snip ?? undefined,
151
+ }));
152
+ }
153
+ getNode(nodeId) {
154
+ const row = this.db.prepare('SELECT * FROM kg_nodes WHERE node_id = ?').get(nodeId);
155
+ return row ? rowToNode(row) : null;
156
+ }
157
+ getNodesByKind(kind, limit = 100) {
158
+ const rows = this.db.prepare('SELECT * FROM kg_nodes WHERE kind = ? ORDER BY name LIMIT ?').all(kind, limit);
159
+ return rows.map((r) => rowToNode(r));
160
+ }
161
+ recordFeedback(row) {
162
+ this.db.prepare(`
163
+ INSERT INTO kg_feedback (id, ts, user, question, answer_kind, block_id, rating, comment)
164
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
165
+ `).run(row.id, row.ts, row.user, row.question, row.answerKind, row.blockId ?? null, row.rating, row.comment ?? null);
166
+ }
167
+ blockFeedbackScore(blockId) {
168
+ const row = this.db.prepare(`
169
+ SELECT
170
+ SUM(CASE WHEN rating = 'up' THEN 1 ELSE 0 END) AS up,
171
+ SUM(CASE WHEN rating = 'down' THEN 1 ELSE 0 END) AS down
172
+ FROM kg_feedback WHERE block_id = ?
173
+ `).get(blockId);
174
+ return {
175
+ up: row?.up ?? 0,
176
+ down: row?.down ?? 0,
177
+ };
178
+ }
179
+ /** Surface high-confidence uncertified answers ready for promotion. */
180
+ promotionCandidates(minUps = 5) {
181
+ const rows = this.db.prepare(`
182
+ SELECT block_id, question, COUNT(*) AS ups
183
+ FROM kg_feedback
184
+ WHERE answer_kind = 'uncertified' AND rating = 'up' AND block_id IS NOT NULL
185
+ GROUP BY block_id, question
186
+ HAVING ups >= ?
187
+ AND NOT EXISTS (
188
+ SELECT 1 FROM kg_feedback f2
189
+ WHERE f2.block_id = kg_feedback.block_id
190
+ AND f2.answer_kind = 'uncertified'
191
+ AND f2.rating = 'down'
192
+ )
193
+ ORDER BY ups DESC
194
+ LIMIT 50
195
+ `).all(minUps);
196
+ return rows.map((r) => ({ blockId: r.block_id, question: r.question, ups: r.ups }));
197
+ }
198
+ meta(key) {
199
+ const row = this.db.prepare('SELECT value FROM kg_meta WHERE key = ?').get(key);
200
+ return row?.value ?? null;
201
+ }
202
+ close() {
203
+ this.db.close();
204
+ }
205
+ }
206
+ function rowToNode(row) {
207
+ return {
208
+ nodeId: row.node_id,
209
+ kind: row.kind,
210
+ name: row.name,
211
+ domain: row.domain ?? undefined,
212
+ status: row.status ?? undefined,
213
+ owner: row.owner ?? undefined,
214
+ description: row.description ?? undefined,
215
+ llmContext: row.llm_context ?? undefined,
216
+ tags: safeJSON(row.tags_json, []),
217
+ examples: safeJSON(row.examples_json, []),
218
+ sourcePath: row.source_path ?? undefined,
219
+ gitSha: row.git_sha ?? undefined,
220
+ };
221
+ }
222
+ function safeJSON(raw, fallback) {
223
+ if (!raw)
224
+ return fallback;
225
+ try {
226
+ return JSON.parse(raw);
227
+ }
228
+ catch {
229
+ return fallback;
230
+ }
231
+ }
232
+ /**
233
+ * Defang FTS5 query syntax we don't trust from arbitrary user input.
234
+ * Allows AND/OR/NOT/quotes/parens and column-prefix syntax (`name:foo`).
235
+ * Strips anything else to whitespace, then collapses.
236
+ */
237
+ function sanitizeFtsQuery(raw) {
238
+ // Drop characters FTS5 might interpret as operators we don't want exposed,
239
+ // except for quotes (allows phrase search) and a couple of column scopers.
240
+ const cleaned = raw
241
+ .replace(/[*]/g, ' ') // strip wildcard injection
242
+ .replace(/\s+/g, ' ')
243
+ .trim();
244
+ if (!cleaned)
245
+ return '';
246
+ // If no quotes and no boolean keyword, fall back to a tokenized OR query
247
+ // for better recall — FTS5 default is implicit AND.
248
+ if (!/["()]|\b(AND|OR|NOT|NEAR)\b/.test(cleaned)) {
249
+ return cleaned
250
+ .split(/\s+/)
251
+ .map((t) => t.replace(/[^\w]/g, ''))
252
+ .filter(Boolean)
253
+ .join(' OR ');
254
+ }
255
+ return cleaned;
256
+ }
257
+ //# sourceMappingURL=sqlite-fts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-fts.js","sourceRoot":"","sources":["../../src/kg/sqlite-fts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Knowledge graph node + edge shapes for the DQL agent.
3
+ *
4
+ * The KG is built from the compiled `dql-manifest.json` (blocks, dashboards,
5
+ * apps, metrics, dimensions, sources) plus dbt manifest data and the project's
6
+ * Skills folder. It is stored in SQLite at `.dql/cache/agent-kg.sqlite` with
7
+ * an FTS5 index for keyword search and structured filters for domain/kind.
8
+ */
9
+ export type KGNodeKind = 'block' | 'metric' | 'dimension' | 'domain' | 'dbt_model' | 'dbt_source' | 'dashboard' | 'app' | 'skill';
10
+ export interface KGNode {
11
+ /** Stable identifier — `${kind}:${name}` (lower-cased). */
12
+ nodeId: string;
13
+ kind: KGNodeKind;
14
+ name: string;
15
+ domain?: string;
16
+ status?: string;
17
+ owner?: string;
18
+ description?: string;
19
+ tags?: string[];
20
+ /** Free-form natural-language context for the LLM (block.llmContext, skill body, etc.). */
21
+ llmContext?: string;
22
+ /** Question/SQL pairs the agent can few-shot on. */
23
+ examples?: Array<{
24
+ question: string;
25
+ sql?: string;
26
+ }>;
27
+ /** Path on disk so the UI can deep-link. */
28
+ sourcePath?: string;
29
+ /** Pinned git SHA at index time. */
30
+ gitSha?: string;
31
+ }
32
+ export interface KGEdge {
33
+ src: string;
34
+ dst: string;
35
+ kind: 'feeds_into' | 'reads_from' | 'aggregates' | 'visualizes' | 'depends_on' | 'contains' | 'related_to';
36
+ weight?: number;
37
+ }
38
+ export interface KGSearchHit {
39
+ node: KGNode;
40
+ /** BM25-derived score (lower is better in raw FTS5; we expose 1/(1+rank) so higher is better). */
41
+ score: number;
42
+ /** Match snippets per field, useful for UI surfacing. */
43
+ snippet?: string;
44
+ }
45
+ export interface KGFeedbackRow {
46
+ id: string;
47
+ ts: string;
48
+ user: string;
49
+ question: string;
50
+ answerKind: 'certified' | 'uncertified';
51
+ blockId?: string;
52
+ rating: 'up' | 'down';
53
+ comment?: string;
54
+ }
55
+ export interface KGSearchOptions {
56
+ query: string;
57
+ /** Restrict to specific kinds. Defaults to all. */
58
+ kinds?: KGNodeKind[];
59
+ /** Restrict to a domain. */
60
+ domain?: string;
61
+ /** Maximum hits to return. */
62
+ limit?: number;
63
+ }
64
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/kg/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,UAAU,GAClB,OAAO,GACP,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,WAAW,GACX,YAAY,GACZ,WAAW,GACX,KAAK,GACL,OAAO,CAAC;AAEZ,MAAM,WAAW,MAAM;IACrB,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,2FAA2F;IAC3F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,UAAU,GAAG,YAAY,CAAC;IAC3G,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,kGAAkG;IAClG,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,WAAW,GAAG,aAAa,CAAC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,IAAI,GAAG,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Knowledge graph node + edge shapes for the DQL agent.
3
+ *
4
+ * The KG is built from the compiled `dql-manifest.json` (blocks, dashboards,
5
+ * apps, metrics, dimensions, sources) plus dbt manifest data and the project's
6
+ * Skills folder. It is stored in SQLite at `.dql/cache/agent-kg.sqlite` with
7
+ * an FTS5 index for keyword search and structured filters for domain/kind.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/kg/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,21 @@
1
+ import type { AgentProvider, AgentMessage, ProviderRunOptions } from './types.js';
2
+ /**
3
+ * Anthropic Claude provider via the Messages API. Reads ANTHROPIC_API_KEY.
4
+ *
5
+ * We deliberately avoid `@anthropic-ai/sdk` to keep dql-agent zero-dep —
6
+ * the existing `apps/cli/src/llm/providers/claude-agent-sdk.ts` already
7
+ * uses the SDK for the chat-cell agent loop, and that's a different layer.
8
+ * Here we want a simple "generate from a message list" surface.
9
+ */
10
+ export declare class ClaudeProvider implements AgentProvider {
11
+ readonly name: "claude";
12
+ private readonly apiKey?;
13
+ private readonly defaultModel;
14
+ constructor(opts?: {
15
+ apiKey?: string;
16
+ model?: string;
17
+ });
18
+ available(): Promise<boolean>;
19
+ generate(messages: AgentMessage[], options?: ProviderRunOptions): Promise<string>;
20
+ }
21
+ //# sourceMappingURL=claude.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/providers/claude.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAEpB;;;;;;;GAOG;AACH,qBAAa,cAAe,YAAW,aAAa;IAClD,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO;IAKpD,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAI7B,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC;CAoC5F"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Anthropic Claude provider via the Messages API. Reads ANTHROPIC_API_KEY.
3
+ *
4
+ * We deliberately avoid `@anthropic-ai/sdk` to keep dql-agent zero-dep —
5
+ * the existing `apps/cli/src/llm/providers/claude-agent-sdk.ts` already
6
+ * uses the SDK for the chat-cell agent loop, and that's a different layer.
7
+ * Here we want a simple "generate from a message list" surface.
8
+ */
9
+ export class ClaudeProvider {
10
+ name = 'claude';
11
+ apiKey;
12
+ defaultModel;
13
+ constructor(opts = {}) {
14
+ this.apiKey = opts.apiKey ?? process.env.ANTHROPIC_API_KEY;
15
+ this.defaultModel = opts.model ?? 'claude-opus-4-7';
16
+ }
17
+ async available() {
18
+ return Boolean(this.apiKey);
19
+ }
20
+ async generate(messages, options = {}) {
21
+ if (!this.apiKey) {
22
+ throw new Error('claude: ANTHROPIC_API_KEY is not set');
23
+ }
24
+ const system = messages.filter((m) => m.role === 'system').map((m) => m.content).join('\n\n');
25
+ const turns = messages
26
+ .filter((m) => m.role !== 'system')
27
+ .map((m) => ({ role: m.role, content: m.content }));
28
+ const res = await fetch('https://api.anthropic.com/v1/messages', {
29
+ method: 'POST',
30
+ headers: {
31
+ 'x-api-key': this.apiKey,
32
+ 'anthropic-version': '2023-06-01',
33
+ 'content-type': 'application/json',
34
+ },
35
+ body: JSON.stringify({
36
+ model: options.model ?? this.defaultModel,
37
+ max_tokens: options.maxTokens ?? 1024,
38
+ temperature: options.temperature ?? 0.2,
39
+ system: system || undefined,
40
+ messages: turns,
41
+ }),
42
+ signal: options.signal,
43
+ });
44
+ if (!res.ok) {
45
+ const body = await res.text().catch(() => res.statusText);
46
+ throw new Error(`claude: ${res.status} ${body}`);
47
+ }
48
+ const json = (await res.json());
49
+ const blocks = json.content ?? [];
50
+ return blocks
51
+ .filter((b) => b.type === 'text')
52
+ .map((b) => b.text ?? '')
53
+ .join('');
54
+ }
55
+ }
56
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/providers/claude.ts"],"names":[],"mappings":"AAMA;;;;;;;GAOG;AACH,MAAM,OAAO,cAAc;IAChB,IAAI,GAAG,QAAiB,CAAC;IACjB,MAAM,CAAU;IAChB,YAAY,CAAS;IAEtC,YAAY,OAA4C,EAAE;QACxD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QAC3D,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,IAAI,iBAAiB,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,SAAS;QACb,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,QAAwB,EAAE,UAA8B,EAAE;QACvE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9F,MAAM,KAAK,GAAG,QAAQ;aACnB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;aAClC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAEtD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,uCAAuC,EAAE;YAC/D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,WAAW,EAAE,IAAI,CAAC,MAAM;gBACxB,mBAAmB,EAAE,YAAY;gBACjC,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY;gBACzC,UAAU,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI;gBACrC,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,GAAG;gBACvC,MAAM,EAAE,MAAM,IAAI,SAAS;gBAC3B,QAAQ,EAAE,KAAK;aAChB,CAAC;YACF,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC1D,MAAM,IAAI,KAAK,CAAC,WAAW,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QACnD,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAyD,CAAC;QACxF,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;QAClC,OAAO,MAAM;aACV,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;aAChC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;aACxB,IAAI,CAAC,EAAE,CAAC,CAAC;IACd,CAAC;CACF"}
@@ -0,0 +1,18 @@
1
+ import type { AgentProvider, AgentMessage, ProviderRunOptions } from './types.js';
2
+ /**
3
+ * Google Gemini provider via the v1beta REST surface. Reads GEMINI_API_KEY.
4
+ * Maps system messages by prepending them to the first user turn since
5
+ * Gemini's API doesn't have a first-class system role.
6
+ */
7
+ export declare class GeminiProvider implements AgentProvider {
8
+ readonly name: "gemini";
9
+ private readonly apiKey?;
10
+ private readonly defaultModel;
11
+ constructor(opts?: {
12
+ apiKey?: string;
13
+ model?: string;
14
+ });
15
+ available(): Promise<boolean>;
16
+ generate(messages: AgentMessage[], options?: ProviderRunOptions): Promise<string>;
17
+ }
18
+ //# sourceMappingURL=gemini.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/providers/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAEpB;;;;GAIG;AACH,qBAAa,cAAe,YAAW,aAAa;IAClD,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO;IAKpD,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAI7B,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC;CA4C5F"}