@devflow-tools/context-engine 0.16.34 → 0.17.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 (50) hide show
  1. package/dist/channel-query-planner.d.ts +9 -0
  2. package/dist/channel-query-planner.d.ts.map +1 -0
  3. package/dist/channel-query-planner.js +92 -0
  4. package/dist/channel-query-planner.js.map +1 -0
  5. package/dist/context-budget-allocator.d.ts +19 -0
  6. package/dist/context-budget-allocator.d.ts.map +1 -0
  7. package/dist/context-budget-allocator.js +35 -0
  8. package/dist/context-budget-allocator.js.map +1 -0
  9. package/dist/context-engine.d.ts.map +1 -1
  10. package/dist/context-engine.js +107 -12
  11. package/dist/context-engine.js.map +1 -1
  12. package/dist/create-engines.d.ts.map +1 -1
  13. package/dist/create-engines.js +17 -5
  14. package/dist/create-engines.js.map +1 -1
  15. package/dist/graph-adapter.d.ts +2 -0
  16. package/dist/graph-adapter.d.ts.map +1 -1
  17. package/dist/graph-adapter.js +14 -1
  18. package/dist/graph-adapter.js.map +1 -1
  19. package/dist/graph-evidence-selector.d.ts +26 -0
  20. package/dist/graph-evidence-selector.d.ts.map +1 -0
  21. package/dist/graph-evidence-selector.js +77 -0
  22. package/dist/graph-evidence-selector.js.map +1 -0
  23. package/dist/index.d.ts +5 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +3 -0
  26. package/dist/index.js.map +1 -1
  27. package/dist/indexer-adapter.d.ts.map +1 -1
  28. package/dist/indexer-adapter.js +7 -1
  29. package/dist/indexer-adapter.js.map +1 -1
  30. package/dist/intent-parser.d.ts.map +1 -1
  31. package/dist/intent-parser.js +11 -9
  32. package/dist/intent-parser.js.map +1 -1
  33. package/dist/retrieval-handlers.d.ts +3 -0
  34. package/dist/retrieval-handlers.d.ts.map +1 -1
  35. package/dist/retrieval-handlers.js +69 -2
  36. package/dist/retrieval-handlers.js.map +1 -1
  37. package/dist/retrieval-ledger.d.ts.map +1 -1
  38. package/dist/retrieval-ledger.js +60 -4
  39. package/dist/retrieval-ledger.js.map +1 -1
  40. package/dist/retrieval-task-plan.d.ts +8 -1
  41. package/dist/retrieval-task-plan.d.ts.map +1 -1
  42. package/dist/retrieval-task-plan.js +67 -36
  43. package/dist/retrieval-task-plan.js.map +1 -1
  44. package/dist/task-retrieval-orchestrator.d.ts +9 -0
  45. package/dist/task-retrieval-orchestrator.d.ts.map +1 -1
  46. package/dist/task-retrieval-orchestrator.js +33 -7
  47. package/dist/task-retrieval-orchestrator.js.map +1 -1
  48. package/dist/types.d.ts +4 -1
  49. package/dist/types.d.ts.map +1 -1
  50. package/package.json +7 -7
@@ -0,0 +1,9 @@
1
+ import type { ChannelQueryPlan, ImmutableTaskIntentArtifact } from '@devflow-tools/sdk';
2
+ import type { RetrievalTaskPlan } from './retrieval-task-plan.js';
3
+ export declare function buildChannelQueryPlan(input: {
4
+ artifact: ImmutableTaskIntentArtifact;
5
+ taskPlan: RetrievalTaskPlan;
6
+ expandedTerms?: string;
7
+ generatedBy?: ChannelQueryPlan['generatedBy'];
8
+ }): ChannelQueryPlan;
9
+ //# sourceMappingURL=channel-query-planner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-query-planner.d.ts","sourceRoot":"","sources":["../src/channel-query-planner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAEhB,2BAA2B,EAG5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,QAAQ,EAAE,2BAA2B,CAAC;IACtC,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAC;CAC/C,GAAG,gBAAgB,CA6CnB"}
@@ -0,0 +1,92 @@
1
+ import { createHash } from 'node:crypto';
2
+ export function buildChannelQueryPlan(input) {
3
+ const authoritativeQuery = taskQuery(input.artifact);
4
+ const expanded = boundedQuery(input.expandedTerms);
5
+ const retrievalQuery = expanded ? `${authoritativeQuery} ${expanded}` : authoritativeQuery;
6
+ const code = input.artifact.intent === 'memory'
7
+ ? []
8
+ : [{
9
+ query: retrievalQuery,
10
+ targetAnchors: [...input.artifact.targetAnchors],
11
+ symbols: [...input.taskPlan.symbols],
12
+ relationKinds: relationKinds(input.taskPlan),
13
+ maxDepth: input.artifact.targetAnchors.length > 0 ? 1 : 2,
14
+ }];
15
+ const memory = [{
16
+ query: authoritativeQuery,
17
+ scopes: input.artifact.activeSkill ? ['project', 'skill', 'session'] : ['project', 'session'],
18
+ entities: [...input.taskPlan.entities],
19
+ facets: [...input.taskPlan.requiredPolicyFacets],
20
+ requireVerifiedEvidence: true,
21
+ }];
22
+ const knowledge = input.taskPlan.knowledgeNeeds.length > 0
23
+ ? input.taskPlan.knowledgeNeeds.map(need => ({
24
+ query: queryForKnowledgeNeed(need, authoritativeQuery),
25
+ requirement: need.required ? 'knowledge_required' : 'knowledge_optional',
26
+ kind: need.kind,
27
+ ...(need.entity ? { entity: need.entity } : {}),
28
+ ...(need.symbol ? { symbol: need.symbol } : {}),
29
+ ...(need.version ? { version: need.version } : {}),
30
+ }))
31
+ : [{
32
+ query: '',
33
+ requirement: 'knowledge_not_required',
34
+ kind: 'overview',
35
+ }];
36
+ const createdAt = input.artifact.createdAt;
37
+ const planBody = {
38
+ identity: input.artifact.identity,
39
+ sourceIntentHash: input.artifact.artifactHash,
40
+ code,
41
+ memory,
42
+ knowledge,
43
+ generatedBy: input.generatedBy ?? (expanded ? 'hybrid' : 'deterministic'),
44
+ createdAt,
45
+ };
46
+ return { ...planBody, planHash: stableHash(planBody) };
47
+ }
48
+ function taskQuery(artifact) {
49
+ const withoutCommand = artifact.command
50
+ ? artifact.normalizedPrompt.replace(new RegExp(`^\\/${escapeRegExp(artifact.command)}\\b`, 'iu'), '').trim()
51
+ : artifact.normalizedPrompt;
52
+ return boundedQuery(withoutCommand) || boundedQuery(artifact.rawPrompt) || artifact.action;
53
+ }
54
+ function boundedQuery(value) {
55
+ return value?.normalize('NFKC').replace(/\s+/gu, ' ').trim().slice(0, 2_000) ?? '';
56
+ }
57
+ function relationKinds(plan) {
58
+ const kinds = new Set(['dependency']);
59
+ if (['debug', 'modify', 'analyze'].includes(plan.action))
60
+ kinds.add('caller');
61
+ if (plan.intent === 'performance')
62
+ kinds.add('caller');
63
+ if (plan.action === 'create') {
64
+ kinds.add('route');
65
+ kinds.add('config');
66
+ }
67
+ if (plan.action === 'test')
68
+ kinds.add('test');
69
+ return [...kinds];
70
+ }
71
+ function queryForKnowledgeNeed(need, fallback) {
72
+ return [need.entity, need.symbol, need.kind, need.version].filter(Boolean).join(' ') || fallback;
73
+ }
74
+ function stableHash(value) {
75
+ return createHash('sha256').update(stableJson(value)).digest('hex');
76
+ }
77
+ function stableJson(value) {
78
+ if (value === null || typeof value === 'string' || typeof value === 'boolean')
79
+ return JSON.stringify(value);
80
+ if (typeof value === 'number')
81
+ return Number.isFinite(value) ? JSON.stringify(value) : 'null';
82
+ if (Array.isArray(value))
83
+ return `[${value.map(stableJson).join(',')}]`;
84
+ if (!value || typeof value !== 'object')
85
+ return JSON.stringify(String(value));
86
+ const record = value;
87
+ return `{${Object.keys(record).sort().map(key => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(',')}}`;
88
+ }
89
+ function escapeRegExp(value) {
90
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
91
+ }
92
+ //# sourceMappingURL=channel-query-planner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-query-planner.js","sourceRoot":"","sources":["../src/channel-query-planner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export interface BudgetedContextCandidate {
2
+ path: string;
3
+ score: number;
4
+ source: 'structural' | 'graph';
5
+ }
6
+ export interface ContextBudgetAllocation {
7
+ selectedPaths: Set<string>;
8
+ rejectedPaths: Set<string>;
9
+ maxFiles: number;
10
+ graphFileLimit: number;
11
+ selectedGraphFiles: number;
12
+ }
13
+ export declare function allocateContextFileBudget(input: {
14
+ requiredPaths: Iterable<string>;
15
+ candidates: BudgetedContextCandidate[];
16
+ tokenBudget: number;
17
+ graphRatio?: number;
18
+ }): ContextBudgetAllocation;
19
+ //# sourceMappingURL=context-budget-allocator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-budget-allocator.d.ts","sourceRoot":"","sources":["../src/context-budget-allocator.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE;IAC/C,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChC,UAAU,EAAE,wBAAwB,EAAE,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,uBAAuB,CAqB1B"}
@@ -0,0 +1,35 @@
1
+ export function allocateContextFileBudget(input) {
2
+ const required = new Set(input.requiredPaths);
3
+ const maxFiles = Math.max(required.size, Math.max(5, Math.min(24, Math.floor(input.tokenBudget / 280))));
4
+ const remaining = Math.max(0, maxFiles - required.size);
5
+ const graphFileLimit = Math.min(remaining, Math.max(1, Math.floor(maxFiles * (input.graphRatio ?? 0.2))));
6
+ const structural = dedupe(input.candidates.filter(candidate => candidate.source === 'structural'));
7
+ const graph = dedupe(input.candidates.filter(candidate => candidate.source === 'graph'));
8
+ const selectedPaths = new Set(required);
9
+ for (const candidate of structural) {
10
+ if (selectedPaths.size >= maxFiles)
11
+ break;
12
+ selectedPaths.add(candidate.path);
13
+ }
14
+ let selectedGraphFiles = 0;
15
+ for (const candidate of graph) {
16
+ if (selectedPaths.size >= maxFiles || selectedGraphFiles >= graphFileLimit)
17
+ break;
18
+ if (selectedPaths.has(candidate.path))
19
+ continue;
20
+ selectedPaths.add(candidate.path);
21
+ selectedGraphFiles += 1;
22
+ }
23
+ const rejectedPaths = new Set(input.candidates.map(candidate => candidate.path).filter(path => !selectedPaths.has(path)));
24
+ return { selectedPaths, rejectedPaths, maxFiles, graphFileLimit, selectedGraphFiles };
25
+ }
26
+ function dedupe(candidates) {
27
+ const byPath = new Map();
28
+ for (const candidate of candidates) {
29
+ const existing = byPath.get(candidate.path);
30
+ if (!existing || candidate.score > existing.score)
31
+ byPath.set(candidate.path, candidate);
32
+ }
33
+ return [...byPath.values()].sort((left, right) => right.score - left.score || left.path.localeCompare(right.path));
34
+ }
35
+ //# sourceMappingURL=context-budget-allocator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-budget-allocator.js","sourceRoot":"","sources":["../src/context-budget-allocator.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"context-engine.d.ts","sourceRoot":"","sources":["../src/context-engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EAEb,WAAW,EACX,aAAa,EAOb,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,eAAe,EAA0B,MAAM,iCAAiC,CAAC;AAC/F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAwB,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAQzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAU3D,OAAO,KAAK,EAKV,kBAAkB,EAClB,+BAA+B,EAChC,MAAM,YAAY,CAAC;AAuSpB,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,SAAS,CAAC;IACd,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,eAAe,CAAC;IAC3B,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAoCD,qBAAa,aAAa;IACxB,OAAO,CAAC,EAAE,CAAY;IACtB,OAAO,CAAC,IAAI,CAAgB;IAC5B,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,CAAoB;IACrC,OAAO,CAAC,WAAW,CAAC,CAAoB;IACxC,OAAO,CAAC,cAAc,CAAC,CAAiB;gBAE5B,IAAI,EAAE,iBAAiB;IAYnC,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,UAAU,CAAkD;IAE9D,gCAAgC,CAAC,KAAK,EAAE;QAC5C,OAAO,EAAE,sBAAsB,EAAE,CAAC;QAClC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,cAAc,EAAE,MAAM,CAAC;KACxB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIX,KAAK,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,OAAO,CAAC,yBAAyB;YA4BnB,yBAAyB;IAiCvC,OAAO,CAAC,eAAe;IAKjB,KAAK,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAmRtD,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,+BAA+B,CAAC;YA4iB/F,sBAAsB;IAmWpC,OAAO,CAAC,yBAAyB;YAgBnB,2BAA2B;YA4B3B,6BAA6B;IAkB3C,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,sBAAsB;IAM9B,OAAO,CAAC,2BAA2B;IAOnC,SAAS,IAAI,WAAW;CASzB"}
1
+ {"version":3,"file":"context-engine.d.ts","sourceRoot":"","sources":["../src/context-engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EAEb,WAAW,EACX,aAAa,EAOb,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,eAAe,EAA0B,MAAM,iCAAiC,CAAC;AAC/F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAwB,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAQzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAU3D,OAAO,KAAK,EAKV,kBAAkB,EAClB,+BAA+B,EAChC,MAAM,YAAY,CAAC;AA0SpB,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,SAAS,CAAC;IACd,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,eAAe,CAAC;IAC3B,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAoCD,qBAAa,aAAa;IACxB,OAAO,CAAC,EAAE,CAAY;IACtB,OAAO,CAAC,IAAI,CAAgB;IAC5B,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,CAAoB;IACrC,OAAO,CAAC,WAAW,CAAC,CAAoB;IACxC,OAAO,CAAC,cAAc,CAAC,CAAiB;gBAE5B,IAAI,EAAE,iBAAiB;IAYnC,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,UAAU,CAAkD;IAE9D,gCAAgC,CAAC,KAAK,EAAE;QAC5C,OAAO,EAAE,sBAAsB,EAAE,CAAC;QAClC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,cAAc,EAAE,MAAM,CAAC;KACxB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIX,KAAK,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,OAAO,CAAC,yBAAyB;YAoCnB,yBAAyB;IAiCvC,OAAO,CAAC,eAAe;IAKjB,KAAK,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAkRtD,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,+BAA+B,CAAC;YA8nB/F,sBAAsB;IAsWpC,OAAO,CAAC,yBAAyB;YAgBnB,2BAA2B;YA4B3B,6BAA6B;IAkB3C,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,sBAAsB;IAM9B,OAAO,CAAC,2BAA2B;IAOnC,SAAS,IAAI,WAAW;CASzB"}
@@ -11,6 +11,9 @@ import { reachableCodeGraphNodeIds, sanitizeCodeGraphEdges, } from './graph-edge
11
11
  import { assembleContextFileRoles } from './context-file-roles.js';
12
12
  import { buildTaskContextEnvelope } from './task-retrieval-orchestrator.js';
13
13
  import { recordTaskEnvelopeLedger } from './retrieval-ledger.js';
14
+ import { selectGraphEvidence } from './graph-evidence-selector.js';
15
+ import { allocateContextFileBudget } from './context-budget-allocator.js';
16
+ import { emitSemanticControlLog } from '@devflow-tools/telemetry';
14
17
  const CG = CGModule.default ?? CGModule;
15
18
  const SYMBOL_VECTOR_WARNING = "Symbol vector search is not ready; using CodeGraph/BM25 results. Run `devflow doctor` for details.";
16
19
  // ── RRF (Reciprocal Rank Fusion) ─────────────────────────────────
@@ -317,6 +320,12 @@ export class ContextEngine {
317
320
  relevantFiles: options.relevantFiles,
318
321
  action: taskPlan?.action,
319
322
  knowledgeNeeds: taskPlan?.knowledgeNeeds,
323
+ sessionId: options.sessionId,
324
+ executionId: options.executionId,
325
+ requestId: options.requestId,
326
+ turnId: options.turnId,
327
+ intentArtifactHash: options.intentArtifact?.artifactHash,
328
+ queryPlanHash: options.queryPlan?.planHash,
320
329
  projectSignals: {
321
330
  frameworks,
322
331
  languages,
@@ -622,15 +631,27 @@ export class ContextEngine {
622
631
  }
623
632
  const intent = parseIntent(query);
624
633
  const taskType = intent.intent;
625
- const combinedQuery = mergeQueries(query, expandQuery(query), options?.expandedTerms);
626
- const targetAnchors = extractTargetAnchors(query);
634
+ const combinedQuery = options?.queryPlan?.code[0]?.query
635
+ ?? mergeQueries(query, expandQuery(query), options?.expandedTerms);
636
+ const targetAnchors = options?.intentArtifact?.targetAnchors ?? extractTargetAnchors(query);
627
637
  const retrievalTaskPlan = buildRetrievalTaskPlan({
628
638
  query,
629
639
  parsedIntent: intent,
630
640
  targetAnchors,
631
641
  activeSkill: options?.activeSkill,
632
642
  command: options?.command,
643
+ authoritativeArtifact: options?.intentArtifact,
633
644
  });
645
+ const projectRoot = this.cg.getProjectRoot?.() ?? process.cwd();
646
+ const logIdentity = {
647
+ projectRoot,
648
+ sessionId: options?.sessionId,
649
+ turnId: options?.turnId,
650
+ requestId: options?.requestId,
651
+ executionId: options?.executionId,
652
+ intentArtifactHash: options?.intentArtifact?.artifactHash,
653
+ queryPlanHash: options?.queryPlan?.planHash,
654
+ };
634
655
  const timing = {};
635
656
  const t0 = Date.now();
636
657
  const [cgResult, knowledgeResult, memoryHits] = await Promise.all([
@@ -645,7 +666,10 @@ export class ContextEngine {
645
666
  }),
646
667
  new Promise((_, reject) => setTimeout(() => reject(new Error("buildContext timeout")), 15000)),
647
668
  ]).catch((err) => {
648
- console.error("[devflow] cg.buildContext failed, falling back:", err.message);
669
+ emitSemanticControlLog({
670
+ event: 'context.build.degraded', identity: logIdentity, level: 'warn', timestamp: Date.now(),
671
+ data: { stage: 'codegraph_context', reason: err.message, fallback: 'manual_pipeline' },
672
+ });
649
673
  return null;
650
674
  })
651
675
  : Promise.resolve(null),
@@ -653,7 +677,10 @@ export class ContextEngine {
653
677
  this.searchKnowledgeWithStatus(combinedQuery, this.getKnowledgeSearchOptions(combinedQuery, 5, options, retrievalTaskPlan, maxTokens)),
654
678
  new Promise((_, reject) => setTimeout(() => reject(new Error("knowledge timeout")), 5000)),
655
679
  ]).catch((err) => {
656
- console.error("[devflow] knowledge.search timeout:", err.message);
680
+ emitSemanticControlLog({
681
+ event: 'knowledge.search.degraded', identity: logIdentity, level: 'warn', timestamp: Date.now(),
682
+ data: { reason: err.message, fallback: 'knowledge_empty' },
683
+ });
657
684
  return { entries: [], warnings: [`knowledge-search-failed: ${err.message}`] };
658
685
  }),
659
686
  typeof this.memory.search === "function"
@@ -668,18 +695,23 @@ export class ContextEngine {
668
695
  }),
669
696
  new Promise((_, reject) => setTimeout(() => reject(new Error("memory timeout")), 5000)),
670
697
  ]).catch((err) => {
671
- console.error("[devflow] memory.search timeout:", err.message);
698
+ emitSemanticControlLog({
699
+ event: 'memory.search.completed', identity: logIdentity, level: 'warn', timestamp: Date.now(),
700
+ data: { status: 'degraded', reason: err.message, selected: 0, fallback: 'memory_empty' },
701
+ });
672
702
  return [];
673
703
  })
674
704
  : Promise.resolve([]),
675
705
  ]);
676
706
  timing["1_promise_all"] = Date.now() - t0;
677
- console.error("[devflow:timing] Promise.all:", timing["1_promise_all"], "ms");
678
707
  const knowledgeHits = knowledgeResult.entries;
679
708
  if (knowledgeResult.warnings.length > 0)
680
709
  warnings.push(...knowledgeResult.warnings);
681
710
  if (!cgResult) {
682
- console.error("[devflow] buildContext failed, falling back to manual pipeline");
711
+ emitSemanticControlLog({
712
+ event: 'context.build.degraded', identity: logIdentity, level: 'warn', timestamp: Date.now(),
713
+ data: { stage: 'codegraph_context', reason: 'context_unavailable', fallback: 'manual_pipeline' },
714
+ });
683
715
  const result = await this.buildTaskContextLegacy(query, options);
684
716
  result._devflow = { fallback: "buildContext-unavailable" };
685
717
  return result;
@@ -691,7 +723,10 @@ export class ContextEngine {
691
723
  ctx = JSON.parse(cgResult);
692
724
  }
693
725
  catch {
694
- console.error("[devflow] buildContext returned unparseable string, falling back");
726
+ emitSemanticControlLog({
727
+ event: 'context.build.degraded', identity: logIdentity, level: 'warn', timestamp: Date.now(),
728
+ data: { stage: 'codegraph_parse', reason: 'unparseable_context', fallback: 'manual_pipeline' },
729
+ });
695
730
  return this.buildTaskContextLegacy(query, options);
696
731
  }
697
732
  }
@@ -732,7 +767,6 @@ export class ContextEngine {
732
767
  }
733
768
  }
734
769
  }
735
- console.error("[devflow:timing] Fallback searchNodes loop:", Date.now() - tFallback, "ms");
736
770
  timing["2_fallback_loop"] = Date.now() - tFallback;
737
771
  const fileFreshnessByPath = new Map();
738
772
  if (this.indexer?.ensureFilesCurrent) {
@@ -770,7 +804,24 @@ export class ContextEngine {
770
804
  const anchoredEntryPoints = targetAnchors.length > 0
771
805
  ? originalEntryPoints.filter(entry => fileMatchesTargetAnchor(entry.filePath, targetAnchors))
772
806
  : originalEntryPoints;
773
- const effectiveEntryPoints = anchoredEntryPoints.length > 0 ? anchoredEntryPoints : originalEntryPoints;
807
+ const graphSelection = selectGraphEvidence({
808
+ codegraph: this.cg,
809
+ candidates: originalEntryPoints,
810
+ symbols: retrievalTaskPlan.symbols,
811
+ targetAnchors,
812
+ });
813
+ const evidenceEntryPoints = graphSelection.selected.length > 0
814
+ ? graphSelection.selected
815
+ : originalEntryPoints;
816
+ const selectedAnchoredEntryPoints = targetAnchors.length > 0
817
+ ? evidenceEntryPoints.filter(entry => fileMatchesTargetAnchor(entry.filePath, targetAnchors))
818
+ : evidenceEntryPoints;
819
+ const effectiveEntryPoints = selectedAnchoredEntryPoints.length > 0
820
+ ? selectedAnchoredEntryPoints
821
+ : anchoredEntryPoints.length > 0 ? anchoredEntryPoints : evidenceEntryPoints;
822
+ if (graphSelection.ambiguities.length > 0) {
823
+ warnings.push(`codegraph-symbol-ambiguity:${graphSelection.ambiguities.length}`);
824
+ }
774
825
  const rootIds = new Set(effectiveEntryPoints.map((ep) => ep.id));
775
826
  const reachableNodeIds = reachableCodeGraphNodeIds(rootIds, edgeQuality.accepted);
776
827
  const entryFiles = new Set(effectiveEntryPoints.map((entry) => entry.filePath));
@@ -792,6 +843,7 @@ export class ContextEngine {
792
843
  const perFileCount = new Map();
793
844
  const MAX_SYMBOLS_PER_FILE = 5;
794
845
  const optionalCandidates = new Map();
846
+ const graphOptionalPaths = new Set();
795
847
  const queryTerms = combinedQuery.split(/\s+/).map(term => term.toLowerCase()).filter(term => term.length >= 3);
796
848
  for (const node of allNodes) {
797
849
  // Skip file wrappers and import edges — they're not actionable symbols
@@ -831,6 +883,7 @@ export class ContextEngine {
831
883
  snippet: "",
832
884
  distance,
833
885
  });
886
+ graphOptionalPaths.add(node.filePath);
834
887
  }
835
888
  }
836
889
  }
@@ -876,6 +929,21 @@ export class ContextEngine {
876
929
  }
877
930
  }
878
931
  let rankedOptionalFiles = rankOptionalContextFiles([...optionalCandidates.values()], taskType);
932
+ const codeBudget = allocateContextFileBudget({
933
+ requiredPaths: requiredFileSet,
934
+ candidates: rankedOptionalFiles.map(candidate => ({
935
+ path: candidate.path,
936
+ score: candidate.score,
937
+ source: fileRoleByPath.has(candidate.path) && fileRoleByPath.get(candidate.path)?.role !== 'dependency'
938
+ ? 'structural'
939
+ : graphOptionalPaths.has(candidate.path) ? 'graph' : 'structural',
940
+ })),
941
+ tokenBudget: Math.floor(maxTokens * 0.45),
942
+ graphRatio: 0.2,
943
+ });
944
+ rankedOptionalFiles = rankedOptionalFiles.filter(candidate => codeBudget.selectedPaths.has(candidate.path));
945
+ if (codeBudget.rejectedPaths.size > 0)
946
+ warnings.push(`codegraph-budget-rejected:${codeBudget.rejectedPaths.size}`);
879
947
  for (const candidate of rankedOptionalFiles)
880
948
  optionalFileSet.add(candidate.path);
881
949
  if (this.indexer?.getFileFreshness) {
@@ -943,7 +1011,6 @@ export class ContextEngine {
943
1011
  const compressed = compressToTokenLimit(allFileSnippets, Math.floor(maxTokens * 0.45));
944
1012
  if (compressed.overBudget)
945
1013
  warnings.push("context-compressor-budget-exceeded");
946
- console.error("[devflow:timing] Snippet building + compress:", Date.now() - tSnippet, "ms");
947
1014
  timing["3_snippet_compress"] = Date.now() - tSnippet;
948
1015
  const riskHints = generateRiskHints(allNodes.filter(node => node.kind !== "file" && node.kind !== "import"), graphEvidence);
949
1016
  const nextActions = generateNextActions(taskType, keySymbols.slice(0, 5));
@@ -1072,9 +1139,18 @@ export class ContextEngine {
1072
1139
  requestId: options?.requestId,
1073
1140
  host: options?.host,
1074
1141
  command: options?.command,
1142
+ turnId: options?.turnId,
1143
+ intentArtifact: options?.intentArtifact,
1144
+ queryPlan: options?.queryPlan,
1075
1145
  tokenBudget: maxTokens,
1076
1146
  targetAnchors,
1077
1147
  taskPlan: retrievalTaskPlan,
1148
+ graphAmbiguities: graphSelection.ambiguities,
1149
+ codeBudget: {
1150
+ maxFiles: codeBudget.maxFiles,
1151
+ graphFileLimit: codeBudget.graphFileLimit,
1152
+ selectedGraphFiles: codeBudget.selectedGraphFiles,
1153
+ },
1078
1154
  fileRoles: fileRoleByPath,
1079
1155
  fileFreshness: fileFreshnessByPath,
1080
1156
  memoryHits,
@@ -1110,7 +1186,23 @@ export class ContextEngine {
1110
1186
  this.queryCache.set(cacheKey, result);
1111
1187
  }
1112
1188
  timing["0_total"] = Date.now() - t0;
1113
- console.error("[devflow:timing] buildTaskContext TOTAL:", timing["0_total"], "ms");
1189
+ emitSemanticControlLog({
1190
+ event: 'codegraph.selection.completed',
1191
+ identity: {
1192
+ ...logIdentity,
1193
+ contextReceipt: result.taskEnvelope?.receipt.contextHash,
1194
+ },
1195
+ level: warnings.length > 0 ? 'warn' : 'info',
1196
+ timestamp: Date.now(),
1197
+ data: {
1198
+ selectedFiles: result.taskEnvelope?.code.files.length ?? 0,
1199
+ selectedSymbols: result.taskEnvelope?.code.symbols.length ?? 0,
1200
+ rejectedRelations: result.taskEnvelope?.quality.code.rejected ?? 0,
1201
+ ambiguityCount: result.taskEnvelope?.code.ambiguities?.length ?? 0,
1202
+ timing,
1203
+ warnings: warnings.map(value => value.split(':')[0]),
1204
+ },
1205
+ });
1114
1206
  result._timing = timing;
1115
1207
  return result;
1116
1208
  }
@@ -1445,6 +1537,9 @@ export class ContextEngine {
1445
1537
  requestId: options?.requestId,
1446
1538
  host: options?.host,
1447
1539
  command: options?.command,
1540
+ turnId: options?.turnId,
1541
+ intentArtifact: options?.intentArtifact,
1542
+ queryPlan: options?.queryPlan,
1448
1543
  tokenBudget: maxTokens,
1449
1544
  targetAnchors,
1450
1545
  fileRoles: fileRoleByPath,