@duckcodeailabs/dql-agent 1.13.0 → 1.13.2
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.
- package/dist/agent-run-engine.d.ts +8 -6
- package/dist/agent-run-engine.d.ts.map +1 -1
- package/dist/agent-run-engine.js +34 -2
- package/dist/agent-run-engine.js.map +1 -1
- package/dist/answer-loop.d.ts +9 -1
- package/dist/answer-loop.d.ts.map +1 -1
- package/dist/answer-loop.js +59 -5
- package/dist/answer-loop.js.map +1 -1
- package/dist/app-builder.d.ts +19 -1
- package/dist/app-builder.d.ts.map +1 -1
- package/dist/app-builder.js +261 -51
- package/dist/app-builder.js.map +1 -1
- package/dist/cascade/route-policy.d.ts +1 -1
- package/dist/cascade/route-policy.d.ts.map +1 -1
- package/dist/context-authoring.d.ts +98 -0
- package/dist/context-authoring.d.ts.map +1 -0
- package/dist/context-authoring.js +146 -0
- package/dist/context-authoring.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/metadata/analytical-policy.d.ts +8 -0
- package/dist/metadata/analytical-policy.d.ts.map +1 -1
- package/dist/metadata/analytical-policy.js +5 -2
- package/dist/metadata/analytical-policy.js.map +1 -1
- package/dist/metadata/catalog.d.ts +2 -1
- package/dist/metadata/catalog.d.ts.map +1 -1
- package/dist/metadata/catalog.js +106 -14
- package/dist/metadata/catalog.js.map +1 -1
- package/dist/metadata/meaning-evidence.d.ts +0 -5
- package/dist/metadata/meaning-evidence.d.ts.map +1 -1
- package/dist/metadata/meaning-evidence.js +57 -0
- package/dist/metadata/meaning-evidence.js.map +1 -1
- package/dist/propose/build-from-prompt.d.ts.map +1 -1
- package/dist/propose/build-from-prompt.js +27 -0
- package/dist/propose/build-from-prompt.js.map +1 -1
- package/dist/skills/loader.d.ts +24 -0
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +137 -6
- package/dist/skills/loader.js.map +1 -1
- package/package.json +4 -4
package/dist/answer-loop.js
CHANGED
|
@@ -959,6 +959,7 @@ export async function answer(input) {
|
|
|
959
959
|
: selectRelevantSkills(executionInput.skills ?? [], executionInput.question, {
|
|
960
960
|
userId: executionInput.userId ?? null,
|
|
961
961
|
modelAreaIds: executionInput.domainContext?.modelAreaId ? [executionInput.domainContext.modelAreaId] : [],
|
|
962
|
+
focusObjectKeys: executionInput.contextPack?.focusObjectKey ? [executionInput.contextPack.focusObjectKey] : [],
|
|
962
963
|
domains: Array.from(new Set([
|
|
963
964
|
...domainContextSearchDomains(executionInput.domainContext),
|
|
964
965
|
...(executionInput.domain ? [executionInput.domain] : []),
|
|
@@ -1062,6 +1063,7 @@ async function runAnswerLoop(input) {
|
|
|
1062
1063
|
userId: userId ?? null,
|
|
1063
1064
|
domains: inferredDomains,
|
|
1064
1065
|
modelAreaIds: input.domainContext?.modelAreaId ? [input.domainContext.modelAreaId] : [],
|
|
1066
|
+
focusObjectKeys: input.contextPack?.focusObjectKey ? [input.contextPack.focusObjectKey] : [],
|
|
1065
1067
|
});
|
|
1066
1068
|
const effectiveBlockHints = Array.from(new Set([
|
|
1067
1069
|
...blockHints,
|
|
@@ -1757,7 +1759,11 @@ async function runAnswerLoop(input) {
|
|
|
1757
1759
|
domainContext: input.domainContext,
|
|
1758
1760
|
})
|
|
1759
1761
|
: undefined;
|
|
1760
|
-
|
|
1762
|
+
// A multi-entity question gets the certified join plan; a single-entity one
|
|
1763
|
+
// gets that entity's authored grain and business meaning. Before this, only
|
|
1764
|
+
// the former existed, so most everyday questions saw no modeling at all.
|
|
1765
|
+
const analyticalPlanPrompt = renderAnalyticalPlanPrompt(analyticalPlan)
|
|
1766
|
+
?? renderSingleEntityModelingPrompt(analyticalPlan, input.manifest);
|
|
1761
1767
|
if (analyticalPlanPrompt)
|
|
1762
1768
|
messages.push({ role: 'system', content: analyticalPlanPrompt });
|
|
1763
1769
|
if (questionDomainScope.length > 0) {
|
|
@@ -4185,7 +4191,35 @@ function entityIdentifierTokensForMatching(entity) {
|
|
|
4185
4191
|
.flatMap((value) => value.toLowerCase().replace(/^(fct|dim|stg)_/, '').split(/[^a-z0-9]+/))
|
|
4186
4192
|
.filter((token) => token.length > 2 && token !== 'entity');
|
|
4187
4193
|
}
|
|
4188
|
-
|
|
4194
|
+
/**
|
|
4195
|
+
* CTX-008: the authored business identity of a single modeled entity. Most
|
|
4196
|
+
* real questions name exactly one entity, and those used to receive no modeling
|
|
4197
|
+
* context at all because the join planner needs two. Grain and business context
|
|
4198
|
+
* matter just as much to a one-table answer as keys do to a join.
|
|
4199
|
+
*/
|
|
4200
|
+
export function renderSingleEntityModelingPrompt(plan, manifest) {
|
|
4201
|
+
if (!plan || plan.entities.length !== 1 || !manifest?.modeling)
|
|
4202
|
+
return undefined;
|
|
4203
|
+
const entity = manifest.modeling.entities[plan.entities[0]];
|
|
4204
|
+
if (!entity)
|
|
4205
|
+
return undefined;
|
|
4206
|
+
const relation = manifest.dbtProvenance?.nodes[entity.dbtUniqueId]?.relation;
|
|
4207
|
+
const lines = [
|
|
4208
|
+
`- ${entity.businessName || entity.localId}${relation ? ` (${relation})` : ''}`,
|
|
4209
|
+
entity.businessContext ? ` meaning: ${entity.businessContext}` : '',
|
|
4210
|
+
entity.grain ? ` grain: one row per ${entity.grain}` : '',
|
|
4211
|
+
entity.keys.length ? ` keys: ${entity.keys.join(', ')}` : '',
|
|
4212
|
+
entity.analyticalRole && entity.analyticalRole !== 'unknown' ? ` role: ${entity.analyticalRole}` : '',
|
|
4213
|
+
].filter(Boolean);
|
|
4214
|
+
if (lines.length === 1 && !entity.businessName)
|
|
4215
|
+
return undefined;
|
|
4216
|
+
return [
|
|
4217
|
+
'DQL MODELED ENTITY (authored business context):',
|
|
4218
|
+
...lines,
|
|
4219
|
+
'Respect the stated grain: do not aggregate across it in a way that double counts, and use the business meaning to interpret the question.',
|
|
4220
|
+
].join('\n');
|
|
4221
|
+
}
|
|
4222
|
+
export function renderAnalyticalPlanPrompt(plan) {
|
|
4189
4223
|
if (!plan || plan.entities.length < 2)
|
|
4190
4224
|
return undefined;
|
|
4191
4225
|
if (!plan.safe) {
|
|
@@ -4222,11 +4256,19 @@ function renderAnalyticalPlanPrompt(plan) {
|
|
|
4222
4256
|
...plan.edges.map((edge, index) => [
|
|
4223
4257
|
`${index + 1}. ${edge.fromEntity} (${edge.fromRelation ?? 'bound dbt model'}) -> ${edge.toEntity} (${edge.toRelation ?? 'bound dbt model'})`,
|
|
4224
4258
|
` relationship=${edge.relationshipId}; keys=${edge.keys.map((key) => `${key.from}=${key.to}`).join(', ')}; cardinality=${edge.cardinality}; fanout=${edge.fanout}`,
|
|
4259
|
+
// CTX-008: the authored meaning travels with the keys, so the model can
|
|
4260
|
+
// tell a technically valid join from the one the question actually wants.
|
|
4261
|
+
analyticalEdgeMeaning(edge),
|
|
4225
4262
|
edge.importRefs.length ? ` imports=${edge.importRefs.join(', ')}` : '',
|
|
4226
4263
|
].filter(Boolean).join('\n')),
|
|
4227
4264
|
'Use only these relationships and exact key pairs. dbt DAG lineage and same-named columns are not join authorization. Any different join must be refused.',
|
|
4228
4265
|
].join('\n');
|
|
4229
4266
|
}
|
|
4267
|
+
/** One `means:` line describing what a join represents, when it was authored. */
|
|
4268
|
+
function analyticalEdgeMeaning(edge) {
|
|
4269
|
+
const meaning = [edge.verb, edge.description, edge.rationale].filter(Boolean).join(' — ');
|
|
4270
|
+
return meaning ? ` means: ${meaning}` : '';
|
|
4271
|
+
}
|
|
4230
4272
|
/**
|
|
4231
4273
|
* Missing modeling may enter the narrow EXP-001 host-validated lane. Explicit
|
|
4232
4274
|
* unsafe, cross-domain, attribution, expiry, purpose, and proof failures never do.
|
|
@@ -4560,10 +4602,22 @@ function renderCandidateJoinsForPrompt(contextPack, budget) {
|
|
|
4560
4602
|
...joins.map((join) => `- ${join.leftRelation}.${join.leftColumn} -> ${join.rightRelation}.${join.rightColumn}${join.reason ? ` (${join.reason})` : ''}`),
|
|
4561
4603
|
].join('\n');
|
|
4562
4604
|
}
|
|
4605
|
+
/**
|
|
4606
|
+
* CTX-008: `proves_join` edges are the governed relationship graph the user
|
|
4607
|
+
* authored, and the quick budget sets `edgeLimit: 0` — which blacked them out
|
|
4608
|
+
* entirely on exactly the lookup questions that most need them. Governed join
|
|
4609
|
+
* proof always earns a small allowance; every other edge class still respects
|
|
4610
|
+
* the budget.
|
|
4611
|
+
*/
|
|
4612
|
+
const GOVERNED_JOIN_EDGE_FLOOR = 12;
|
|
4563
4613
|
function renderContextEdgesForPrompt(contextPack, budget) {
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4614
|
+
const governed = contextPack.edges
|
|
4615
|
+
.filter((edge) => edge.edgeType === 'proves_join')
|
|
4616
|
+
.slice(0, Math.max(budget.edgeLimit, GOVERNED_JOIN_EDGE_FLOOR));
|
|
4617
|
+
const others = budget.edgeLimit > 0
|
|
4618
|
+
? contextPack.edges.filter((edge) => edge.edgeType !== 'proves_join').slice(0, budget.edgeLimit)
|
|
4619
|
+
: [];
|
|
4620
|
+
const edges = [...governed, ...others];
|
|
4567
4621
|
if (edges.length === 0)
|
|
4568
4622
|
return '';
|
|
4569
4623
|
return [
|