@duckcodeailabs/dql-agent 1.10.0 → 1.10.1

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 (78) hide show
  1. package/dist/agent-run-engine.d.ts.map +1 -1
  2. package/dist/agent-run-engine.js +6 -1
  3. package/dist/agent-run-engine.js.map +1 -1
  4. package/dist/analytical-compatibility.d.ts +61 -0
  5. package/dist/analytical-compatibility.d.ts.map +1 -0
  6. package/dist/analytical-compatibility.js +515 -0
  7. package/dist/analytical-compatibility.js.map +1 -0
  8. package/dist/analytical-execution-graph.d.ts +174 -0
  9. package/dist/analytical-execution-graph.d.ts.map +1 -0
  10. package/dist/analytical-execution-graph.js +599 -0
  11. package/dist/analytical-execution-graph.js.map +1 -0
  12. package/dist/analytical-failure-repair.d.ts +91 -0
  13. package/dist/analytical-failure-repair.d.ts.map +1 -0
  14. package/dist/analytical-failure-repair.js +325 -0
  15. package/dist/analytical-failure-repair.js.map +1 -0
  16. package/dist/analytical-frame.d.ts +19 -0
  17. package/dist/analytical-frame.d.ts.map +1 -0
  18. package/dist/analytical-frame.js +299 -0
  19. package/dist/analytical-frame.js.map +1 -0
  20. package/dist/analytical-period-resolution.d.ts +53 -0
  21. package/dist/analytical-period-resolution.d.ts.map +1 -0
  22. package/dist/analytical-period-resolution.js +218 -0
  23. package/dist/analytical-period-resolution.js.map +1 -0
  24. package/dist/analytical-readiness.d.ts +17 -0
  25. package/dist/analytical-readiness.d.ts.map +1 -0
  26. package/dist/analytical-readiness.js +95 -0
  27. package/dist/analytical-readiness.js.map +1 -0
  28. package/dist/analytical-result-facts.d.ts +79 -0
  29. package/dist/analytical-result-facts.d.ts.map +1 -0
  30. package/dist/analytical-result-facts.js +301 -0
  31. package/dist/analytical-result-facts.js.map +1 -0
  32. package/dist/answer-loop.d.ts +51 -4
  33. package/dist/answer-loop.d.ts.map +1 -1
  34. package/dist/answer-loop.js +866 -35
  35. package/dist/answer-loop.js.map +1 -1
  36. package/dist/governed-relational-compiler.d.ts +51 -0
  37. package/dist/governed-relational-compiler.d.ts.map +1 -1
  38. package/dist/governed-relational-compiler.js +144 -1
  39. package/dist/governed-relational-compiler.js.map +1 -1
  40. package/dist/index.d.ts +16 -4
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +9 -2
  43. package/dist/index.js.map +1 -1
  44. package/dist/kg/build.d.ts +2 -2
  45. package/dist/kg/build.d.ts.map +1 -1
  46. package/dist/kg/build.js +250 -10
  47. package/dist/kg/build.js.map +1 -1
  48. package/dist/meaning-resolution.d.ts +13 -0
  49. package/dist/meaning-resolution.d.ts.map +1 -1
  50. package/dist/meaning-resolution.js +69 -0
  51. package/dist/meaning-resolution.js.map +1 -1
  52. package/dist/metadata/analysis-planner.js +3 -1
  53. package/dist/metadata/analysis-planner.js.map +1 -1
  54. package/dist/metadata/catalog.d.ts +4 -2
  55. package/dist/metadata/catalog.d.ts.map +1 -1
  56. package/dist/metadata/catalog.js +227 -9
  57. package/dist/metadata/catalog.js.map +1 -1
  58. package/dist/metadata/meaning-evidence.d.ts +9 -2
  59. package/dist/metadata/meaning-evidence.d.ts.map +1 -1
  60. package/dist/metadata/meaning-evidence.js +105 -7
  61. package/dist/metadata/meaning-evidence.js.map +1 -1
  62. package/dist/plan-execution-adapter.d.ts +72 -1
  63. package/dist/plan-execution-adapter.d.ts.map +1 -1
  64. package/dist/plan-execution-adapter.js +208 -0
  65. package/dist/plan-execution-adapter.js.map +1 -1
  66. package/dist/resolved-analytical-plan.d.ts +14 -3
  67. package/dist/resolved-analytical-plan.d.ts.map +1 -1
  68. package/dist/resolved-analytical-plan.js +74 -20
  69. package/dist/resolved-analytical-plan.js.map +1 -1
  70. package/dist/router.d.ts +1 -1
  71. package/dist/router.d.ts.map +1 -1
  72. package/dist/router.js +167 -21
  73. package/dist/router.js.map +1 -1
  74. package/dist/skills/loader.d.ts +17 -0
  75. package/dist/skills/loader.d.ts.map +1 -1
  76. package/dist/skills/loader.js +62 -4
  77. package/dist/skills/loader.js.map +1 -1
  78. package/package.json +4 -4
@@ -11,10 +11,10 @@ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync
11
11
  import { createRequire } from 'node:module';
12
12
  import { dirname, join } from 'node:path';
13
13
  import { brotliCompressSync, brotliDecompressSync, constants as zlibConstants } from 'node:zlib';
14
- import { buildManifest, buildManifestKnowledgeGraph, loadDomainPackageRegistry, loadManifestKnowledgeSkills, loadProjectConfig, parseContractRef, resolveDataLexManifestPath, resolveDbtManifestPath, resolveSemanticLayerWithDiagnostics, extractColumnLineage, normalizeDqlArtifactReference, composeEffectiveTrust, } from '@duckcodeailabs/dql-core';
15
- import { buildKGFromManifest, buildKGFromSemanticLayer } from '../kg/build.js';
16
- import { domainContextSearchDomains } from '../domain-context.js';
17
- import { buildBlockBusinessFingerprint, buildBlockSqlFingerprints } from './block-fingerprints.js';
14
+ import { buildManifest, buildManifestKnowledgeGraph, loadDomainPackageRegistry, loadManifestKnowledgeSkills, loadProjectConfig, parseContractRef, resolveDataLexManifestPath, resolveDbtManifestPath, resolveSemanticLayerWithDiagnostics, extractColumnLineage, normalizeDqlArtifactReference, composeEffectiveTrust, normalizeMetricCapabilityContract, } from "@duckcodeailabs/dql-core";
15
+ import { buildKGFromManifest, buildKGFromSemanticLayer } from "../kg/build.js";
16
+ import { domainContextSearchDomains, } from "../domain-context.js";
17
+ import { buildBlockBusinessFingerprint, buildBlockSqlFingerprints, } from "./block-fingerprints.js";
18
18
  import { buildAnalysisQuestionPlan, certifiedApplicabilityForObject, scoreAllowedSqlRelationWithAnalysisPlan, scoreMetadataObjectWithAnalysisPlan, sortAllowedSqlContextForAnalysisPlan, } from './analysis-planner.js';
19
19
  import { extractSimpleSelectShape, sourceSqlShapeColumns } from './sql-shape.js';
20
20
  import { grainMatches, requestedGrainFromPlan, } from './grain-gate.js';
@@ -835,6 +835,7 @@ export function buildMetadataSnapshot(projectRoot, manifest, semanticLayer, skil
835
835
  addRawDbtManifestCatalogObjects(projectRoot, manifest, objects, edges, diagnostics);
836
836
  addBlockDependencyEdges(manifest, edges);
837
837
  addBlockOutputLineageObjects(manifest, objects, edges);
838
+ addCertifiedBlockAnalyticalCapabilities(objects);
838
839
  // Manifest v3 is the unified DQL runtime. DataLex is migration input only;
839
840
  // retaining it here would reintroduce a second analytical source of truth.
840
841
  if (manifest.manifestVersion !== 3) {
@@ -878,6 +879,171 @@ export function buildMetadataSnapshot(projectRoot, manifest, semanticLayer, skil
878
879
  snapshot.fingerprint = fingerprintSnapshot(snapshot);
879
880
  return snapshot;
880
881
  }
882
+ /**
883
+ * Upgrade only fully declared certified blocks into RFC 0005 capabilities.
884
+ * Local/display-name matching is intentionally insufficient: every reusable
885
+ * metric, grain, dimension/filter, and output role must bind exactly to the
886
+ * already-normalized semantic capability in this immutable snapshot.
887
+ *
888
+ * Acceptance: CONTRACT-002, AGT-018.
889
+ */
890
+ function addCertifiedBlockAnalyticalCapabilities(objects) {
891
+ const semanticCapabilities = [...objects.values()].flatMap((object) => {
892
+ if (object.objectType !== "semantic_metric")
893
+ return [];
894
+ const capability = normalizeMetricCapabilityContract(object.payload?.analyticalCapability);
895
+ if (!capability)
896
+ return [];
897
+ const payload = object.payload ?? {};
898
+ return [
899
+ {
900
+ object,
901
+ capability,
902
+ exactRefs: new Set(uniqueNonBlank([
903
+ capability.metricId,
904
+ object.objectKey,
905
+ object.fullName,
906
+ stringValue(payload.qualifiedId),
907
+ stringValue(payload.sourceNativeId),
908
+ ...metadataStringArray(payload.aliases),
909
+ ])),
910
+ },
911
+ ];
912
+ });
913
+ for (const block of objects.values()) {
914
+ if (block.objectType !== "dql_block" || block.status !== "certified")
915
+ continue;
916
+ const payload = block.payload ?? {};
917
+ const metricRefs = uniqueNonBlank([
918
+ ...metadataStringArray(payload.metricRefs),
919
+ ]);
920
+ if (metricRefs.length !== 1)
921
+ continue;
922
+ const metrics = semanticCapabilities.filter((candidate) => candidate.exactRefs.has(metricRefs[0]));
923
+ if (metrics.length !== 1)
924
+ continue;
925
+ const semantic = metrics[0];
926
+ const grain = stringValue(payload.grain);
927
+ if (!grain || !semantic.capability.resultGrainIds.includes(grain))
928
+ continue;
929
+ const groupedRefs = uniqueNonBlank([
930
+ ...metadataStringArray(payload.dimensions),
931
+ ...metadataStringArray(payload.dimensionsRef),
932
+ ]);
933
+ const filterRefs = uniqueNonBlank(metadataStringArray(payload.allowedFilters));
934
+ const declaredRefs = new Set([...groupedRefs, ...filterRefs]);
935
+ const capabilityDimensionIds = new Set(semantic.capability.dimensions.map((item) => item.dimensionId));
936
+ const capabilityTimeIds = new Set(semantic.capability.timeDimensions.map((item) => item.dimensionId));
937
+ if ([...declaredRefs].some((id) => !capabilityDimensionIds.has(id) && !capabilityTimeIds.has(id)))
938
+ continue;
939
+ const dimensions = semantic.capability.dimensions.flatMap((dimension) => {
940
+ if (!declaredRefs.has(dimension.dimensionId))
941
+ return [];
942
+ const roles = dimension.supportedRoles.filter((role) => (role === "filter" && filterRefs.includes(dimension.dimensionId)) ||
943
+ (role === "group_by" &&
944
+ groupedRefs.includes(dimension.dimensionId)) ||
945
+ (role === "display" &&
946
+ groupedRefs.includes(dimension.dimensionId)) ||
947
+ (role === "rank_entity" &&
948
+ payload.pattern === "ranking" &&
949
+ groupedRefs.includes(dimension.dimensionId)));
950
+ return roles.length > 0
951
+ ? [{ ...dimension, supportedRoles: roles }]
952
+ : [];
953
+ });
954
+ if ([...declaredRefs].some((id) => capabilityDimensionIds.has(id) &&
955
+ !dimensions.some((dimension) => dimension.dimensionId === id)))
956
+ continue;
957
+ const timeDimensions = semantic.capability.timeDimensions.filter((dimension) => declaredRefs.has(dimension.dimensionId));
958
+ const outputs = analyticalBlockOutputs(payload.outputContract);
959
+ if (!outputs ||
960
+ outputs.ids.length === 0 ||
961
+ !outputs.kinds.includes("metric_value"))
962
+ continue;
963
+ const operations = [];
964
+ if (filterRefs.length > 0)
965
+ operations.push("filter");
966
+ if (groupedRefs.some((id) => capabilityDimensionIds.has(id)))
967
+ operations.push("group");
968
+ if (payload.pattern === "trend")
969
+ operations.push("trend");
970
+ if (payload.pattern === "ranking" && outputs.kinds.includes("rank"))
971
+ operations.push("rank");
972
+ if (outputs.kinds.includes("delta") ||
973
+ outputs.kinds.includes("percent_delta"))
974
+ operations.push("compare");
975
+ const blockId = stringValue(payload.qualifiedId) ?? block.objectKey;
976
+ const fingerprint = stringValue(recordPayload(payload.businessFingerprint)?.hash) ??
977
+ sha256(stableStringify({
978
+ blockId,
979
+ metricRefs,
980
+ grain,
981
+ groupedRefs,
982
+ filterRefs,
983
+ outputs,
984
+ }));
985
+ const capability = {
986
+ ...semantic.capability,
987
+ defaultResultGrainId: grain,
988
+ resultGrainIds: uniqueNonBlank([
989
+ grain,
990
+ ...dimensions.map((dimension) => dimension.entityId),
991
+ ]),
992
+ dimensions,
993
+ timeDimensions,
994
+ operations: [...new Set(operations)],
995
+ supportedOutputKinds: outputs.kinds,
996
+ declaredOutputIds: outputs.ids,
997
+ executionCapabilities: [
998
+ { route: "certified", adapterId: block.objectKey },
999
+ ],
1000
+ sourceFingerprint: fingerprint.startsWith("sha256:")
1001
+ ? fingerprint
1002
+ : `sha256:${fingerprint}`,
1003
+ };
1004
+ const normalized = normalizeMetricCapabilityContract(capability);
1005
+ if (!normalized)
1006
+ continue;
1007
+ objects.set(block.objectKey, {
1008
+ ...block,
1009
+ payload: { ...payload, analyticalCapability: normalized },
1010
+ });
1011
+ }
1012
+ }
1013
+ function analyticalBlockOutputs(value) {
1014
+ if (!Array.isArray(value) || value.length === 0)
1015
+ return undefined;
1016
+ const ids = [];
1017
+ const kinds = [];
1018
+ for (const item of value) {
1019
+ const output = recordPayload(item);
1020
+ const id = stringValue(output?.name);
1021
+ const role = stringValue(output?.role)?.toLowerCase();
1022
+ if (!id || !role)
1023
+ return undefined;
1024
+ const kind = role === "metric" || role === "measure" || role === "value"
1025
+ ? "metric_value"
1026
+ : role === "dimension" || role === "grain"
1027
+ ? "dimension"
1028
+ : role === "rank"
1029
+ ? "rank"
1030
+ : role === "delta"
1031
+ ? "delta"
1032
+ : role === "percent_delta"
1033
+ ? "percent_delta"
1034
+ : undefined;
1035
+ if (!kind)
1036
+ return undefined;
1037
+ ids.push(id);
1038
+ kinds.push(kind);
1039
+ }
1040
+ return { ids: uniqueNonBlank(ids), kinds: [...new Set(kinds)] };
1041
+ }
1042
+ function recordPayload(value) {
1043
+ return value && typeof value === "object" && !Array.isArray(value)
1044
+ ? value
1045
+ : undefined;
1046
+ }
881
1047
  /**
882
1048
  * Manifest graph v2 intentionally omits verbose objects and edges. Snapshot
883
1049
  * construction rematerializes that compiler graph once, then persists the
@@ -982,8 +1148,16 @@ function addManifestKnowledgeGraph(manifest, objects, edges) {
982
1148
  sourceSystem: 'DQL compiled cross-domain policy',
983
1149
  payload: compactObject(route),
984
1150
  });
985
- addMetadataEdge(edges, 'cross_domain_route', keyByGraphId.get(`domain::${route.providerDomainId}`) ?? `domain:${route.providerDomainId}`, objectKey, { state: route.state, reasonCodes: route.reasonCodes });
986
- addMetadataEdge(edges, 'cross_domain_route', objectKey, keyByGraphId.get(`domain::${route.consumerDomainId}`) ?? `domain:${route.consumerDomainId}`, { state: route.state, reasonCodes: route.reasonCodes });
1151
+ addMetadataEdge(edges, "cross_domain_route", keyByGraphId.get(`domain::${route.providerDomainId}`) ??
1152
+ `domain:${route.providerDomainId}`, objectKey, {
1153
+ state: route.state,
1154
+ reasonCodes: route.reasonCodes,
1155
+ });
1156
+ addMetadataEdge(edges, "cross_domain_route", objectKey, keyByGraphId.get(`domain::${route.consumerDomainId}`) ??
1157
+ `domain:${route.consumerDomainId}`, {
1158
+ state: route.state,
1159
+ reasonCodes: route.reasonCodes,
1160
+ });
987
1161
  }
988
1162
  for (const edge of graph.edges ?? []) {
989
1163
  const fromKey = keyByGraphId.get(edge.from) ?? edge.from;
@@ -2407,7 +2581,8 @@ function addSkillObjects(skills, objects, edges) {
2407
2581
  domain: skill.domain,
2408
2582
  domains: skill.domains ?? [],
2409
2583
  modelAreaRefs: skill.modelAreaRefs ?? [],
2410
- status: skill.status ?? 'active',
2584
+ status: skill.status ?? "active",
2585
+ analyticalPolicy: skill.analyticalPolicy,
2411
2586
  bodyHash,
2412
2587
  }));
2413
2588
  const objectKey = `skill:${identity}`;
@@ -2442,6 +2617,7 @@ function addSkillObjects(skills, objects, edges) {
2442
2617
  examples: skill.examples ?? [],
2443
2618
  sourceRefs: skill.sourceRefs ?? [],
2444
2619
  vocabulary: skill.vocabulary,
2620
+ analyticalPolicy: skill.analyticalPolicy,
2445
2621
  bodyHash,
2446
2622
  sourceFingerprint,
2447
2623
  isStarter: skill.isStarter,
@@ -6260,13 +6436,22 @@ function skillFromMetadataObject(object) {
6260
6436
  examples: stringArrayPayload(payload.examples),
6261
6437
  sourceRefs: stringArrayPayload(payload.sourceRefs),
6262
6438
  vocabulary: stringRecordPayload(payload.vocabulary),
6263
- body: '',
6264
- sourcePath: object.sourcePath ?? '',
6439
+ analyticalPolicy: skillAnalyticalPolicyPayload(payload.analyticalPolicy),
6440
+ body: "",
6441
+ sourcePath: object.sourcePath ?? "",
6265
6442
  isStarter: payload.isStarter === true,
6266
6443
  };
6267
6444
  }
6268
6445
  function localContextSkillFromParsed(skill, object) {
6269
6446
  const guidance = skill.body.slice(0, 4_000);
6447
+ const sourceHash = stringPayload(object.payload?.sourceFingerprint);
6448
+ const policy = skill.analyticalPolicy && sourceHash
6449
+ ? {
6450
+ policyId: `${skill.qualifiedId ?? skill.id}#analytical`,
6451
+ sourceHash,
6452
+ ...skill.analyticalPolicy,
6453
+ }
6454
+ : undefined;
6270
6455
  return {
6271
6456
  objectKey: object.objectKey,
6272
6457
  id: skill.id,
@@ -6286,6 +6471,7 @@ function localContextSkillFromParsed(skill, object) {
6286
6471
  requiredFilters: [...(skill.requiredFilters ?? [])],
6287
6472
  clarifyWhen: [...(skill.clarifyWhen ?? [])],
6288
6473
  vocabulary: { ...skill.vocabulary },
6474
+ ...(policy ? { analyticalPolicy: policy } : {}),
6289
6475
  guidance,
6290
6476
  guidanceTruncated: guidance.length < skill.body.length,
6291
6477
  sourceRefs: [...(skill.sourceRefs ?? [])],
@@ -6293,6 +6479,38 @@ function localContextSkillFromParsed(skill, object) {
6293
6479
  sourcePath: object.sourcePath,
6294
6480
  };
6295
6481
  }
6482
+ function skillAnalyticalPolicyPayload(value) {
6483
+ if (!value || typeof value !== "object" || Array.isArray(value))
6484
+ return undefined;
6485
+ const record = value;
6486
+ const completenessPolicy = stringPayload(record.completenessPolicy);
6487
+ const comparisonAlignment = stringPayload(record.comparisonAlignment);
6488
+ const defaultRankingPeriod = stringPayload(record.defaultRankingPeriod);
6489
+ const policy = {
6490
+ metricIds: stringArrayPayload(record.metricIds),
6491
+ timeRole: stringPayload(record.timeRole),
6492
+ calendarId: stringPayload(record.calendarId),
6493
+ timezone: stringPayload(record.timezone),
6494
+ completenessPolicy: completenessPolicy === "partial_current" ||
6495
+ completenessPolicy === "latest_complete" ||
6496
+ completenessPolicy === "closed_period"
6497
+ ? completenessPolicy
6498
+ : undefined,
6499
+ comparisonAlignment: comparisonAlignment === "elapsed_period" ||
6500
+ comparisonAlignment === "calendar_period" ||
6501
+ comparisonAlignment === "fiscal_period"
6502
+ ? comparisonAlignment
6503
+ : undefined,
6504
+ defaultRankingPeriod: defaultRankingPeriod === "current" ||
6505
+ defaultRankingPeriod === "comparison"
6506
+ ? defaultRankingPeriod
6507
+ : undefined,
6508
+ narrativeGuidance: stringArrayPayload(record.narrativeGuidance),
6509
+ };
6510
+ return Object.values(policy).some((item) => Array.isArray(item) ? item.length > 0 : Boolean(item))
6511
+ ? policy
6512
+ : undefined;
6513
+ }
6296
6514
  function stringPayload(value) {
6297
6515
  return typeof value === 'string' && value.trim() ? value : undefined;
6298
6516
  }