@duckcodeailabs/dql-cli 1.6.4 → 1.6.6

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 (42) hide show
  1. package/dist/apps-api.d.ts +19 -0
  2. package/dist/apps-api.d.ts.map +1 -1
  3. package/dist/apps-api.js +335 -15
  4. package/dist/apps-api.js.map +1 -1
  5. package/dist/args.d.ts +11 -0
  6. package/dist/args.d.ts.map +1 -1
  7. package/dist/args.js +21 -0
  8. package/dist/args.js.map +1 -1
  9. package/dist/assets/dql-notebook/assets/index-D_tpetmE.js +3790 -0
  10. package/dist/assets/dql-notebook/index.html +1 -1
  11. package/dist/block-studio-import.js +23 -4
  12. package/dist/block-studio-import.js.map +1 -1
  13. package/dist/commands/agent.d.ts +2 -2
  14. package/dist/commands/agent.d.ts.map +1 -1
  15. package/dist/commands/agent.js +78 -13
  16. package/dist/commands/agent.js.map +1 -1
  17. package/dist/commands/app.d.ts.map +1 -1
  18. package/dist/commands/app.js +3 -2
  19. package/dist/commands/app.js.map +1 -1
  20. package/dist/commands/compile.d.ts +2 -0
  21. package/dist/commands/compile.d.ts.map +1 -1
  22. package/dist/commands/compile.js +33 -1
  23. package/dist/commands/compile.js.map +1 -1
  24. package/dist/commands/import.js +6 -6
  25. package/dist/commands/import.js.map +1 -1
  26. package/dist/commands/sync.d.ts.map +1 -1
  27. package/dist/commands/sync.js +17 -3
  28. package/dist/commands/sync.js.map +1 -1
  29. package/dist/index.js +7 -7
  30. package/dist/llm/providers/dql-agent-provider.d.ts.map +1 -1
  31. package/dist/llm/providers/dql-agent-provider.js +113 -10
  32. package/dist/llm/providers/dql-agent-provider.js.map +1 -1
  33. package/dist/local-runtime.d.ts +8 -1
  34. package/dist/local-runtime.d.ts.map +1 -1
  35. package/dist/local-runtime.js +439 -37
  36. package/dist/local-runtime.js.map +1 -1
  37. package/dist/package.json +10 -10
  38. package/dist/promote-from-draft.d.ts +4 -4
  39. package/dist/promote-from-draft.js +8 -8
  40. package/dist/promote-from-draft.js.map +1 -1
  41. package/package.json +11 -11
  42. package/dist/assets/dql-notebook/assets/index-BFUUTIWF.js +0 -3618
@@ -143,6 +143,19 @@ export declare function createAppPackage(projectRoot: string, input: AppCreateRe
143
143
  ok: false;
144
144
  error: string;
145
145
  };
146
+ declare function selectedBlockContext(context: unknown): Record<string, unknown> | null;
147
+ declare function buildPreviewMetricSnapshot(preview: Record<string, unknown>, fallbackTitle?: string): Record<string, unknown>;
148
+ declare function buildPreviewDriverCards(preview: Record<string, unknown>, intent: LocalAppInvestigationIntent): Array<Record<string, unknown>>;
149
+ declare function buildDeterministicInvestigationSql(projectRoot: string, input: {
150
+ question: string;
151
+ intent: LocalAppInvestigationIntent;
152
+ selected: Record<string, unknown> | null;
153
+ sourceBlockId?: string;
154
+ }): {
155
+ sql: string;
156
+ sourceBlockPath: string;
157
+ sourceBlockName: string;
158
+ } | undefined;
146
159
  declare function loadAppById(projectRoot: string, id: string): {
147
160
  app: AppDocument;
148
161
  dashboards: Array<{
@@ -187,5 +200,11 @@ export declare function previewNotebookForApp(projectRoot: string, appId: string
187
200
  status: number;
188
201
  error: string;
189
202
  };
203
+ export declare const __test__: {
204
+ buildPreviewDriverCards: typeof buildPreviewDriverCards;
205
+ buildPreviewMetricSnapshot: typeof buildPreviewMetricSnapshot;
206
+ buildDeterministicInvestigationSql: typeof buildDeterministicInvestigationSql;
207
+ selectedBlockContext: typeof selectedBlockContext;
208
+ };
190
209
  export {};
191
210
  //# sourceMappingURL=apps-api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"apps-api.d.ts","sourceRoot":"","sources":["../src/apps-api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAQL,KAAK,WAAW,EAGjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAOL,KAAK,2BAA2B,EACjC,MAAM,6BAA6B,CAAC;AAErC,UAAU,GAAG;IACX,GAAG,EAAE,eAAe,CAAC;IACrB,GAAG,EAAE,cAAc,CAAC;IACpB,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,wBAAwB,CAAC,EAAE,CAAC,KAAK,EAAE,iCAAiC,KAAK,OAAO,CAAC,gCAAgC,CAAC,CAAC;IACnH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAolB9D;AAID,KAAK,YAAY,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,WAAW,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC;IACjD,aAAa,EAAE,WAAW,GAAG,aAAa,CAAC;IAC3C,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IACxC,UAAU,EAAE,WAAW,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC;IACnD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,SAAS,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,YAAY,CAAC;QAAC,UAAU,EAAE,WAAW,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,CAAA;KAAE,CAAC,CAAC;IACnJ,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;CACpC,CAAC;AAEF,iBAAS,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,EAAE,CAuC5D;AAED,UAAU,wBAAwB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,UAAU,gBAAgB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;IAC/C,SAAS,CAAC,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,UAAU,kBAAkB;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAsDD,UAAU,iCAAiC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,2BAA2B,CAAC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,gCAAgC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,wBAAwB,GAAG,cAAc,EAAE,CA6CtG;AAED,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CACN;IACE,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC/B,GAAG,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IACvD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAC/B,CA2CA;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,gBAAgB,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA2HpI;AA0+BD,iBAAS,WAAW,CAClB,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,GACT;IACD,GAAG,EAAE,WAAW,CAAC;IACjB,UAAU,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1F,SAAS,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;IACrC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC/B,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,cAAc,EAAE,OAAO,EAAE,CAAC;CAC3B,GAAG,IAAI,CA2BP;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IACnG,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,YAAY,CAAC;IAC5C,UAAU,CAAC,EAAE,WAAW,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC,CA2BD;AAED,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9F;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAyBlH;AAED,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,GACnB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAwD/E"}
1
+ {"version":3,"file":"apps-api.d.ts","sourceRoot":"","sources":["../src/apps-api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAQL,KAAK,WAAW,EAGjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAOL,KAAK,2BAA2B,EACjC,MAAM,6BAA6B,CAAC;AAErC,UAAU,GAAG;IACX,GAAG,EAAE,eAAe,CAAC;IACrB,GAAG,EAAE,cAAc,CAAC;IACpB,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,wBAAwB,CAAC,EAAE,CAAC,KAAK,EAAE,iCAAiC,KAAK,OAAO,CAAC,gCAAgC,CAAC,CAAC;IACnH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAqlB9D;AAID,KAAK,YAAY,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,WAAW,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC;IACjD,aAAa,EAAE,WAAW,GAAG,aAAa,CAAC;IAC3C,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IACxC,UAAU,EAAE,WAAW,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC;IACnD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,SAAS,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,YAAY,CAAC;QAAC,UAAU,EAAE,WAAW,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,CAAA;KAAE,CAAC,CAAC;IACnJ,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;CACpC,CAAC;AAEF,iBAAS,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,EAAE,CAuC5D;AAED,UAAU,wBAAwB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,UAAU,gBAAgB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;IAC/C,SAAS,CAAC,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,UAAU,kBAAkB;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAsDD,UAAU,iCAAiC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,2BAA2B,CAAC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,gCAAgC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,wBAAwB,GAAG,cAAc,EAAE,CA6CtG;AAED,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CACN;IACE,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC/B,GAAG,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IACvD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAC/B,CA6CA;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,gBAAgB,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA2HpI;AAwWD,iBAAS,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAQ9E;AAkHD,iBAAS,0BAA0B,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAiCrH;AAED,iBAAS,uBAAuB,CAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,MAAM,EAAE,2BAA2B,GAClC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAoChC;AA8BD,iBAAS,kCAAkC,CACzC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE;IACL,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,2BAA2B,CAAC;IACpC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GACA;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAoE/E;AAuqBD,iBAAS,WAAW,CAClB,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,GACT;IACD,GAAG,EAAE,WAAW,CAAC;IACjB,UAAU,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1F,SAAS,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;IACrC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC/B,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,cAAc,EAAE,OAAO,EAAE,CAAC;CAC3B,GAAG,IAAI,CA2BP;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IACnG,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,YAAY,CAAC;IAC5C,UAAU,CAAC,EAAE,WAAW,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC,CA2BD;AAED,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9F;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAyBlH;AAED,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,GACnB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAwD/E;AA0TD,eAAO,MAAM,QAAQ;;;;;CAKpB,CAAC"}
package/dist/apps-api.js CHANGED
@@ -48,6 +48,7 @@ export async function handleAppsApi(ctx) {
48
48
  sendJson(res, 400, { error: result.error });
49
49
  return true;
50
50
  }
51
+ await refreshGeneratedMetadata(projectRoot);
51
52
  sendJson(res, 201, result);
52
53
  }
53
54
  catch (err) {
@@ -689,7 +690,7 @@ export async function generateAppPackage(projectRoot, input) {
689
690
  if (!prompt)
690
691
  return { ok: false, error: 'prompt is required' };
691
692
  const selectedBlockIds = unique((input.selectedBlockIds ?? []).map(cleanString).filter(Boolean));
692
- const { KGStore, defaultKgPath, generateAppFromPlan, planAppFromPrompt, reindexProject, validateAppPlan, } = await import('@duckcodeailabs/dql-agent');
693
+ const { KGStore, defaultKgPath, generateAppFromPlan, ensureMetadataCatalogFresh, planAppFromPrompt, reindexProject, validateAppPlan, } = await import('@duckcodeailabs/dql-agent');
693
694
  const kgPath = defaultKgPath(projectRoot);
694
695
  await reindexProject(projectRoot, { kgPath });
695
696
  const kg = new KGStore(kgPath);
@@ -705,6 +706,7 @@ export async function generateAppPackage(projectRoot, input) {
705
706
  const generated = generateAppFromPlan(projectRoot, plan, kg, {
706
707
  overwrite: Boolean(input.force),
707
708
  });
709
+ await ensureMetadataCatalogFresh(projectRoot, { force: true });
708
710
  const app = collectAppsList(projectRoot).find((entry) => entry.id === plan.appId) ?? null;
709
711
  return {
710
712
  ok: true,
@@ -1063,13 +1065,28 @@ async function runAppInvestigation(ctx, storage, investigation, input = {}) {
1063
1065
  const previews = buildContextPreviews(selected);
1064
1066
  let metricSnapshot = buildMetricSnapshot(selected);
1065
1067
  let driverCards = buildDriverCards(selected, intent);
1066
- const agentGeneration = generatedSql
1068
+ const baselineGap = intent === 'diagnose_change' && hasSelectedRows(selected) && !hasComparableTimeBaseline(selected);
1069
+ const metadataOnly = intent === 'trust_gap_review';
1070
+ if (metadataOnly)
1071
+ generatedSql = '';
1072
+ const sourceTileId = investigation.sourceTileId ?? selectedContextString(context, 'tileId');
1073
+ const sourceBlockId = investigation.sourceBlockId ?? selectedContextString(context, 'blockId');
1074
+ const deterministicGeneration = generatedSql || baselineGap || metadataOnly
1075
+ ? undefined
1076
+ : buildDeterministicInvestigationSql(ctx.projectRoot, {
1077
+ question,
1078
+ intent,
1079
+ selected,
1080
+ sourceBlockId,
1081
+ });
1082
+ generatedSql = generatedSql || deterministicGeneration?.sql;
1083
+ const agentGeneration = generatedSql || baselineGap || metadataOnly
1067
1084
  ? undefined
1068
1085
  : await generateInvestigationSql(ctx, {
1069
1086
  appId: investigation.appId,
1070
1087
  dashboardId: investigation.dashboardId ?? selectedString(context, 'dashboardId'),
1071
- sourceTileId: investigation.sourceTileId ?? selectedContextString(context, 'tileId'),
1072
- sourceBlockId: investigation.sourceBlockId ?? selectedContextString(context, 'blockId'),
1088
+ sourceTileId,
1089
+ sourceBlockId,
1073
1090
  title: investigation.title,
1074
1091
  question,
1075
1092
  intent,
@@ -1079,7 +1096,7 @@ async function runAppInvestigation(ctx, storage, investigation, input = {}) {
1079
1096
  const generationError = cleanString(agentGeneration?.executionError);
1080
1097
  const sqlEvidence = agentGeneration?.result
1081
1098
  ? { preview: buildGeneratedSqlPreview(agentGeneration.result, generatedSql), error: generationError || undefined }
1082
- : await runGeneratedSqlPreview(ctx, generatedSql);
1099
+ : metadataOnly ? {} : await runGeneratedSqlPreview(ctx, generatedSql);
1083
1100
  const sqlError = sqlEvidence.error ?? generationError;
1084
1101
  if (sqlEvidence.preview) {
1085
1102
  previews.unshift(sqlEvidence.preview);
@@ -1095,6 +1112,10 @@ async function runAppInvestigation(ctx, storage, investigation, input = {}) {
1095
1112
  generatedSql: generatedSql || undefined,
1096
1113
  sqlExecuted: Boolean(sqlEvidence.preview),
1097
1114
  sqlError,
1115
+ generationSource: baselineGap ? 'missing_baseline' : deterministicGeneration ? 'selected_block_metadata' : agentGeneration?.providerUsed ? 'ai_provider' : generatedSql ? 'provided_sql' : 'context_only',
1116
+ baselineGap,
1117
+ sourceBlockPath: deterministicGeneration?.sourceBlockPath,
1118
+ sourceBlockName: deterministicGeneration?.sourceBlockName,
1098
1119
  providerUsed: agentGeneration?.providerUsed,
1099
1120
  },
1100
1121
  certifiedContext: {
@@ -1102,18 +1123,26 @@ async function runAppInvestigation(ctx, storage, investigation, input = {}) {
1102
1123
  appName: appInfo?.app.name,
1103
1124
  dashboardId: investigation.dashboardId,
1104
1125
  dashboardTitle: selectedString(context, 'dashboardTitle'),
1105
- sourceTileId: investigation.sourceTileId ?? selectedContextString(context, 'tileId'),
1106
- sourceBlockId: investigation.sourceBlockId ?? selectedContextString(context, 'blockId'),
1126
+ sourceTileId,
1127
+ sourceBlockId,
1128
+ sourceBlockPath: deterministicGeneration?.sourceBlockPath ?? selectedString(selected, 'blockPath'),
1107
1129
  certificationStatus: selectedString(selected, 'certificationStatus'),
1108
1130
  },
1109
- assumptions: investigationAssumptions(intent, selected, generatedSql, sqlError),
1131
+ assumptions: [
1132
+ ...investigationAssumptions(intent, selected, generatedSql, sqlError),
1133
+ ...(baselineGap ? ['The selected tile sample does not include at least two comparable time values, so DQL did not invent a change query from an unrelated table.'] : []),
1134
+ ],
1110
1135
  context,
1111
1136
  agentEvidence: agentGeneration?.evidence,
1112
1137
  analysisPlan: agentGeneration?.analysisPlan,
1113
1138
  citations: agentGeneration?.citations,
1114
1139
  };
1115
- const summary = cleanString(agentGeneration?.answer) || buildInvestigationSummary(intent, question, selected, metricSnapshot, driverCards);
1116
- const recommendation = buildInvestigationRecommendation(intent, selected, sqlError);
1140
+ const summary = cleanString(agentGeneration?.answer) || (baselineGap
1141
+ ? buildMissingBaselineSummary(question, selected)
1142
+ : buildInvestigationSummary(intent, question, selected, metricSnapshot, driverCards));
1143
+ const recommendation = baselineGap
1144
+ ? buildMissingBaselineRecommendation(selected)
1145
+ : buildInvestigationRecommendation(intent, selected, sqlError);
1117
1146
  return storage.updateAppInvestigation(investigation.id, {
1118
1147
  title: cleanString(input.question) ? titleFromInvestigation(question, selected) : investigation.title,
1119
1148
  question,
@@ -1174,7 +1203,14 @@ function safeIntentContext(context) {
1174
1203
  }
1175
1204
  function selectedBlockContext(context) {
1176
1205
  const root = asRecord(context);
1177
- return asRecord(root?.selectedBlock);
1206
+ const selected = asRecord(root?.selectedBlock);
1207
+ if (selected)
1208
+ return selected;
1209
+ if (!root)
1210
+ return null;
1211
+ const hasSelectedTileContext = ['blockId', 'blockPath', 'tileId', 'certificationStatus', 'resultSample', 'rowCount']
1212
+ .some((key) => root[key] !== undefined && root[key] !== null);
1213
+ return hasSelectedTileContext ? root : null;
1178
1214
  }
1179
1215
  function selectedContextString(context, key) {
1180
1216
  return selectedString(selectedBlockContext(context), key);
@@ -1226,6 +1262,24 @@ function buildMetricSnapshot(selected) {
1226
1262
  context: selectedString(selected, 'title') ?? 'Selected dashboard tile',
1227
1263
  };
1228
1264
  }
1265
+ function hasSelectedRows(selected) {
1266
+ return selectedRows(selected).length > 0;
1267
+ }
1268
+ function hasComparableTimeBaseline(selected) {
1269
+ const rows = selectedRows(selected);
1270
+ if (rows.length < 2)
1271
+ return false;
1272
+ const columns = selectedColumns(selected, rows);
1273
+ const profile = profileResultColumns(columns, rows);
1274
+ const timeDimension = chooseTimeDimension(profile);
1275
+ if (!timeDimension)
1276
+ return false;
1277
+ const values = new Set(rows
1278
+ .map((row) => row[timeDimension.name])
1279
+ .filter((value) => value !== null && value !== undefined && String(value).trim())
1280
+ .map((value) => String(value)));
1281
+ return values.size >= 2;
1282
+ }
1229
1283
  function buildDriverCards(selected, intent) {
1230
1284
  const rows = selectedRows(selected);
1231
1285
  const columns = selectedColumns(selected, rows);
@@ -1274,7 +1328,8 @@ function buildPreviewMetricSnapshot(preview, fallbackTitle) {
1274
1328
  };
1275
1329
  }
1276
1330
  const currentColumn = pickColumn(numericColumns, [/^current_/i, /current.*(revenue|value|amount|total|orders?)/i])
1277
- ?? pickColumn(numericColumns, [/(revenue|value|amount|total|orders?|count)$/i])
1331
+ ?? pickColumn(numericColumns, [/^total_/i, /(revenue|value|amount|total|orders?|points?|goals?|assists?|rebounds?|score|games_played)$/i])
1332
+ ?? pickColumn(numericColumns, [/(count|row_count)$/i])
1278
1333
  ?? numericColumns[0];
1279
1334
  const baselineColumn = pickColumn(numericColumns, [/^baseline_/i, /baseline.*(revenue|value|amount|total|orders?)/i]);
1280
1335
  const deltaColumn = pickColumn(numericColumns, [/(delta|change|variance|diff|contribution)/i]);
@@ -1302,7 +1357,8 @@ function buildPreviewDriverCards(preview, intent) {
1302
1357
  }
1303
1358
  const numericColumns = columns.filter((column) => rows.some((row) => typeofNumber(row[column]) !== null));
1304
1359
  const contributionColumn = pickColumn(numericColumns, [/(delta|change|variance|diff|contribution)/i])
1305
- ?? pickColumn(numericColumns, [/^current_/i, /(revenue|value|amount|total|orders?|count)$/i])
1360
+ ?? pickColumn(numericColumns, [/^current_/i, /^total_/i, /(revenue|value|amount|total|orders?|points?|goals?|assists?|rebounds?|score|games_played)$/i])
1361
+ ?? pickColumn(numericColumns, [/(count|row_count)$/i])
1306
1362
  ?? numericColumns[0];
1307
1363
  const dimensionColumn = columns.find((column) => column !== contributionColumn && rows.some((row) => typeof row[column] === 'string'));
1308
1364
  if (!contributionColumn) {
@@ -1355,6 +1411,229 @@ function sumNumericRows(rows, column) {
1355
1411
  }
1356
1412
  return found ? total : null;
1357
1413
  }
1414
+ function buildDeterministicInvestigationSql(projectRoot, input) {
1415
+ if (input.intent === 'trust_gap_review')
1416
+ return undefined;
1417
+ const block = resolveSelectedBlock(projectRoot, input.selected, input.sourceBlockId);
1418
+ if (!block)
1419
+ return undefined;
1420
+ const source = readFileSync(join(projectRoot, block.path), 'utf-8');
1421
+ const blockSql = extractDqlQuery(source);
1422
+ if (!blockSql || /\{\{/.test(blockSql) || !isReadOnlySql(blockSql))
1423
+ return undefined;
1424
+ const rows = selectedRows(input.selected);
1425
+ const columns = selectedColumns(input.selected, rows);
1426
+ const sourceSql = stripTopLevelOrderAndLimit(blockSql);
1427
+ const profile = profileResultColumns(columns, rows);
1428
+ const measure = chooseMeasureColumn(profile);
1429
+ if (!measure)
1430
+ return undefined;
1431
+ const dimension = chooseDimensionColumn(input.question, profile, input.intent);
1432
+ const sourceCte = `WITH dql_source AS (\n${sourceSql}\n)`;
1433
+ if (input.intent === 'entity_drilldown') {
1434
+ const entity = inferEntityFilter(input.question, profile, rows);
1435
+ const orderBy = `ORDER BY ${quoteSqlIdentifier(measure.name)} DESC`;
1436
+ const where = entity ? `\nWHERE ${quoteSqlIdentifier(entity.column)} IS NOT NULL AND LOWER(CAST(${quoteSqlIdentifier(entity.column)} AS VARCHAR)) LIKE ${sqlStringLiteral(`%${entity.value.toLowerCase()}%`)}` : '';
1437
+ return {
1438
+ sql: `${sourceCte}\nSELECT *\nFROM dql_source${where}\n${orderBy}\nLIMIT 100`,
1439
+ sourceBlockPath: block.path,
1440
+ sourceBlockName: block.name,
1441
+ };
1442
+ }
1443
+ if (input.intent === 'anomaly_investigation' || input.intent === 'diagnose_change') {
1444
+ const timeDimension = chooseTimeDimension(profile) ?? dimension;
1445
+ const rankExpr = `${measureAgg(measure)}(${quoteSqlIdentifier(measure.name)})`;
1446
+ if (timeDimension) {
1447
+ return {
1448
+ sql: [
1449
+ sourceCte,
1450
+ ', dql_trend AS (',
1451
+ ` SELECT ${quoteSqlIdentifier(timeDimension.name)} AS ${quoteSqlIdentifier(timeDimension.name)}, ${rankExpr} AS ${quoteSqlIdentifier(measure.name)}`,
1452
+ ' FROM dql_source',
1453
+ ` GROUP BY ${quoteSqlIdentifier(timeDimension.name)}`,
1454
+ '), dql_deltas AS (',
1455
+ ` SELECT ${quoteSqlIdentifier(timeDimension.name)}, ${quoteSqlIdentifier(measure.name)}, LAG(${quoteSqlIdentifier(measure.name)}) OVER (ORDER BY ${quoteSqlIdentifier(timeDimension.name)}) AS baseline_${safeAlias(measure.name)}`,
1456
+ ' FROM dql_trend',
1457
+ ')',
1458
+ `SELECT *, ${quoteSqlIdentifier(measure.name)} - baseline_${safeAlias(measure.name)} AS delta_${safeAlias(measure.name)}`,
1459
+ 'FROM dql_deltas',
1460
+ `ORDER BY ABS(COALESCE(delta_${safeAlias(measure.name)}, 0)) DESC`,
1461
+ 'LIMIT 20',
1462
+ ].join('\n'),
1463
+ sourceBlockPath: block.path,
1464
+ sourceBlockName: block.name,
1465
+ };
1466
+ }
1467
+ }
1468
+ if (!dimension)
1469
+ return undefined;
1470
+ const aggregate = `${measureAgg(measure)}(${quoteSqlIdentifier(measure.name)})`;
1471
+ const label = quoteSqlIdentifier(dimension.name);
1472
+ return {
1473
+ sql: [
1474
+ sourceCte,
1475
+ `SELECT ${label} AS ${label}, ${aggregate} AS ${quoteSqlIdentifier(measure.name)}, COUNT(*) AS ${quoteSqlIdentifier('row_count')}`,
1476
+ 'FROM dql_source',
1477
+ `GROUP BY ${label}`,
1478
+ `ORDER BY ABS(COALESCE(${quoteSqlIdentifier(measure.name)}, 0)) DESC`,
1479
+ 'LIMIT 20',
1480
+ ].join('\n'),
1481
+ sourceBlockPath: block.path,
1482
+ sourceBlockName: block.name,
1483
+ };
1484
+ }
1485
+ function resolveSelectedBlock(projectRoot, selected, sourceBlockId) {
1486
+ const selectedPath = selectedString(selected, 'blockPath');
1487
+ const candidates = collectBlockCandidates(projectRoot);
1488
+ if (selectedPath) {
1489
+ const normalizedPath = selectedPath.replace(/^\/+/, '');
1490
+ const found = candidates.find((block) => block.path === normalizedPath);
1491
+ if (found)
1492
+ return found;
1493
+ if (normalizedPath.startsWith('blocks/') && existsSync(join(projectRoot, normalizedPath))) {
1494
+ const source = readFileSync(join(projectRoot, normalizedPath), 'utf-8');
1495
+ const name = matchString(source, /block\s+"([^"]+)"/) ?? titleFromPath(normalizedPath);
1496
+ return {
1497
+ id: name,
1498
+ name,
1499
+ domain: matchString(source, /domain\s*=\s*"([^"]+)"/) ?? 'uncategorized',
1500
+ status: matchString(source, /status\s*=\s*"([^"]+)"/) ?? 'draft',
1501
+ owner: matchString(source, /owner\s*=\s*"([^"]+)"/),
1502
+ tags: matchArray(source, /tags\s*=\s*\[([^\]]*)\]/),
1503
+ path: normalizedPath,
1504
+ lastModified: statSyncSafe(join(projectRoot, normalizedPath))?.mtime.toISOString() ?? new Date(0).toISOString(),
1505
+ description: matchString(source, /description\s*=\s*"((?:[^"\\]|\\.)*)"/) ?? '',
1506
+ llmContext: matchString(source, /llmContext\s*=\s*"((?:[^"\\]|\\.)*)"/),
1507
+ chartType: matchString(source, /chart\s*=\s*"([^"]+)"/) ?? undefined,
1508
+ score: 0,
1509
+ reasons: [],
1510
+ };
1511
+ }
1512
+ }
1513
+ const id = cleanString(sourceBlockId) || selectedString(selected, 'blockId');
1514
+ if (!id)
1515
+ return undefined;
1516
+ return candidates.find((block) => block.id === id || block.name === id || block.path === id);
1517
+ }
1518
+ function profileResultColumns(columns, rows) {
1519
+ return columns.map((name) => {
1520
+ const lower = name.toLowerCase();
1521
+ const numeric = rows.length === 0 ? !isLikelyTextColumn(lower) : rows.some((row) => typeofNumber(row[name]) !== null);
1522
+ const text = rows.some((row) => typeof row[name] === 'string' && String(row[name]).trim().length > 0);
1523
+ const time = /\b(season|year|month|week|quarter|date|day)\b/i.test(lower);
1524
+ const identifier = /\b(id|key|uuid|number)\b/i.test(lower) && !time;
1525
+ const measureName = /\b(total|sum|amount|revenue|sales|points?|goals?|assists?|rebounds?|count|avg|average|rate|pct|percent|score|value|delta|change|variance)\b/i.test(lower);
1526
+ const dimensionName = /\b(name|type|segment|region|market|category|status|player|customer|account|team|season|year|month|week|quarter|date)\b/i.test(lower);
1527
+ const measure = numeric && measureName && !identifier && !time;
1528
+ const dimension = !measure && (text || time || dimensionName || !numeric);
1529
+ return { name, lower, numeric, text, dimension, measure, time };
1530
+ });
1531
+ }
1532
+ function chooseMeasureColumn(columns) {
1533
+ const candidates = columns.filter((column) => column.measure);
1534
+ return candidates.find((column) => /\b(delta|change|variance|contribution)\b/i.test(column.lower))
1535
+ ?? candidates.find((column) => /\b(total_points|total_revenue|total_amount|total|revenue|amount|sales|points|goals)\b/i.test(column.lower))
1536
+ ?? candidates.find((column) => /\b(count|value|score|avg|average|rate|pct|percent)\b/i.test(column.lower))
1537
+ ?? columns.find((column) => column.numeric && !column.dimension);
1538
+ }
1539
+ function chooseDimensionColumn(question, columns, intent) {
1540
+ const dimensions = columns.filter((column) => column.dimension);
1541
+ const questionTokens = new Set(question.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean));
1542
+ const mentioned = dimensions.find((column) => column.lower.split(/[^a-z0-9]+/).some((token) => questionTokens.has(token)));
1543
+ if (mentioned)
1544
+ return mentioned;
1545
+ const timeDimension = chooseTimeDimension(columns);
1546
+ if ((intent === 'diagnose_change' || intent === 'segment_compare' || intent === 'anomaly_investigation') && timeDimension)
1547
+ return timeDimension;
1548
+ return dimensions.find((column) => column.text && !column.time)
1549
+ ?? timeDimension
1550
+ ?? dimensions[0];
1551
+ }
1552
+ function chooseTimeDimension(columns) {
1553
+ return columns.find((column) => column.time);
1554
+ }
1555
+ function inferEntityFilter(question, columns, rows) {
1556
+ const textDimensions = columns.filter((column) => column.dimension && (column.text || /\b(name|player|customer|account|team)\b/i.test(column.lower)));
1557
+ const lowerQuestion = question.toLowerCase();
1558
+ for (const column of textDimensions) {
1559
+ for (const row of rows) {
1560
+ const value = cleanString(row[column.name]);
1561
+ if (value && lowerQuestion.includes(value.toLowerCase()))
1562
+ return { column: column.name, value };
1563
+ }
1564
+ }
1565
+ const named = question.match(/\b([A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)\b/);
1566
+ const value = named?.[1]?.trim();
1567
+ const column = textDimensions[0];
1568
+ return value && column ? { column: column.name, value } : undefined;
1569
+ }
1570
+ function measureAgg(column) {
1571
+ return /\b(avg|average|rate|pct|percent|per_)\b/i.test(column.lower) ? 'AVG' : 'SUM';
1572
+ }
1573
+ function extractDqlQuery(source) {
1574
+ const tripleQuoteMatch = source.match(/query\s*=\s*"""([\s\S]*?)"""/i);
1575
+ if (tripleQuoteMatch)
1576
+ return tripleQuoteMatch[1].trim() || null;
1577
+ const singleQuoteMatch = source.match(/query\s*=\s*"((?:[^"\\]|\\.)*)"/i);
1578
+ if (singleQuoteMatch)
1579
+ return singleQuoteMatch[1].replace(/\\"/g, '"').trim() || null;
1580
+ return null;
1581
+ }
1582
+ function stripTopLevelOrderAndLimit(sql) {
1583
+ let next = sql.trim().replace(/;+\s*$/g, '');
1584
+ const limitIndex = findLastTopLevelKeyword(next, 'limit');
1585
+ if (limitIndex >= 0 && /^\s+limit\s+\d+\s*$/i.test(next.slice(limitIndex))) {
1586
+ next = next.slice(0, limitIndex).trim();
1587
+ }
1588
+ const orderIndex = findLastTopLevelKeyword(next, 'order by');
1589
+ if (orderIndex >= 0)
1590
+ next = next.slice(0, orderIndex).trim();
1591
+ return next;
1592
+ }
1593
+ function findLastTopLevelKeyword(sql, keyword) {
1594
+ const lower = sql.toLowerCase();
1595
+ const target = keyword.toLowerCase();
1596
+ let depth = 0;
1597
+ let quote = null;
1598
+ let last = -1;
1599
+ for (let i = 0; i < lower.length; i += 1) {
1600
+ const char = lower[i];
1601
+ if (quote) {
1602
+ if (char === quote && lower[i - 1] !== '\\')
1603
+ quote = null;
1604
+ continue;
1605
+ }
1606
+ if (char === '"' || char === "'" || char === '`') {
1607
+ quote = char;
1608
+ continue;
1609
+ }
1610
+ if (char === '(')
1611
+ depth += 1;
1612
+ if (char === ')')
1613
+ depth = Math.max(0, depth - 1);
1614
+ if (depth === 0 && lower.startsWith(target, i) && isKeywordBoundary(lower, i - 1) && isKeywordBoundary(lower, i + target.length)) {
1615
+ last = i;
1616
+ }
1617
+ }
1618
+ return last;
1619
+ }
1620
+ function isKeywordBoundary(value, index) {
1621
+ if (index < 0 || index >= value.length)
1622
+ return true;
1623
+ return /[^a-z0-9_]/i.test(value[index]);
1624
+ }
1625
+ function quoteSqlIdentifier(identifier) {
1626
+ return `"${identifier.replace(/"/g, '""')}"`;
1627
+ }
1628
+ function sqlStringLiteral(value) {
1629
+ return `'${value.replace(/'/g, "''")}'`;
1630
+ }
1631
+ function safeAlias(identifier) {
1632
+ return identifier.replace(/[^a-z0-9_]+/gi, '_').replace(/^_+|_+$/g, '') || 'value';
1633
+ }
1634
+ function isLikelyTextColumn(value) {
1635
+ return /\b(name|type|segment|region|market|category|status|player|customer|account|team)\b/i.test(value);
1636
+ }
1358
1637
  async function generateInvestigationSql(ctx, input) {
1359
1638
  if (!ctx.generateInvestigationSql)
1360
1639
  return undefined;
@@ -1432,13 +1711,26 @@ function buildGeneratedSqlPreview(result, generatedSql) {
1432
1711
  };
1433
1712
  }
1434
1713
  function isReadOnlySql(sql) {
1435
- const trimmed = sql.trim().replace(/;+\s*$/g, '');
1714
+ const trimmed = stripLeadingSqlComments(sql).replace(/;+\s*$/g, '');
1436
1715
  if (!/^(select|with)\b/i.test(trimmed))
1437
1716
  return false;
1438
1717
  if (/;\s*\S/.test(trimmed))
1439
1718
  return false;
1440
1719
  return !/\b(insert|update|delete|merge|drop|alter|create|truncate|copy|grant|revoke|call|execute|attach|detach)\b/i.test(trimmed);
1441
1720
  }
1721
+ function stripLeadingSqlComments(sql) {
1722
+ let next = sql.trim();
1723
+ while (next.startsWith('--') || next.startsWith('/*')) {
1724
+ if (next.startsWith('--')) {
1725
+ const lineEnd = next.indexOf('\n');
1726
+ next = lineEnd >= 0 ? next.slice(lineEnd + 1).trimStart() : '';
1727
+ continue;
1728
+ }
1729
+ const blockEnd = next.indexOf('*/');
1730
+ next = blockEnd >= 0 ? next.slice(blockEnd + 2).trimStart() : '';
1731
+ }
1732
+ return next;
1733
+ }
1442
1734
  function boundedPreviewSql(sql) {
1443
1735
  return `SELECT * FROM (${sql.trim().replace(/;+\s*$/g, '')}) AS dql_research_preview LIMIT 100`;
1444
1736
  }
@@ -1487,6 +1779,14 @@ function buildInvestigationSummary(intent, question, selected, metrics, drivers)
1487
1779
  const driver = drivers[0]?.title ? ` Top visible driver in the current evidence is ${drivers[0].title}.` : '';
1488
1780
  return `DQL opened a review-required investigation for ${target}: ${question}.${delta}${driver}`;
1489
1781
  }
1782
+ function buildMissingBaselineSummary(question, selected) {
1783
+ const target = selectedString(selected, 'title') ?? 'the selected tile';
1784
+ return `DQL opened a review-required investigation for ${target}: ${question}. The selected tile shows the current certified result, but its sample does not include a comparable prior period or historical snapshot, so DQL cannot calculate what changed without guessing.`;
1785
+ }
1786
+ function buildMissingBaselineRecommendation(selected) {
1787
+ const target = selectedString(selected, 'title') ?? 'this tile';
1788
+ return `Use ${target} as current-state evidence. To explain change, add or select a block with a time grain, snapshot date, or prior-period baseline, then rerun the investigation.`;
1789
+ }
1490
1790
  function buildInvestigationRecommendation(intent, selected, sqlError) {
1491
1791
  if (sqlError)
1492
1792
  return 'Review the generated SQL or add a certified drilldown block before promoting this result.';
@@ -1525,7 +1825,11 @@ function titleFromInvestigation(question, selected) {
1525
1825
  return base.replace(/\s+/g, ' ').slice(0, 90);
1526
1826
  }
1527
1827
  function selectedRows(selected) {
1528
- const rows = Array.isArray(selected?.sampleRows) ? selected?.sampleRows : selected?.rows;
1828
+ const rows = Array.isArray(selected?.sampleRows)
1829
+ ? selected?.sampleRows
1830
+ : Array.isArray(selected?.resultSample)
1831
+ ? selected?.resultSample
1832
+ : selected?.rows;
1529
1833
  if (!Array.isArray(rows))
1530
1834
  return [];
1531
1835
  return rows.map(asRecord).filter((row) => Boolean(row)).slice(0, 100);
@@ -2187,6 +2491,16 @@ function activatePersona(projectRoot, userId, appId) {
2187
2491
  return null;
2188
2492
  }
2189
2493
  // ---- IO utilities ----
2494
+ async function refreshGeneratedMetadata(projectRoot) {
2495
+ try {
2496
+ const { ensureMetadataCatalogFresh } = await import('@duckcodeailabs/dql-agent');
2497
+ await ensureMetadataCatalogFresh(projectRoot, { force: true });
2498
+ }
2499
+ catch {
2500
+ // App files remain the source of truth; the local catalog refreshes again
2501
+ // on the next agent/MCP call if this best-effort update fails.
2502
+ }
2503
+ }
2190
2504
  function sendJson(res, status, body) {
2191
2505
  res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
2192
2506
  res.end(JSON.stringify(body));
@@ -2209,6 +2523,12 @@ async function readJson(req) {
2209
2523
  req.on('error', reject);
2210
2524
  });
2211
2525
  }
2526
+ export const __test__ = {
2527
+ buildPreviewDriverCards,
2528
+ buildPreviewMetricSnapshot,
2529
+ buildDeterministicInvestigationSql,
2530
+ selectedBlockContext,
2531
+ };
2212
2532
  // reference unused parseAppDocument/readFileSync to keep import stable for forward use
2213
2533
  void parseAppDocument;
2214
2534
  void readFileSync;