agentic-qe 3.8.10 → 3.8.12

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 (119) hide show
  1. package/.claude/skills/skills-manifest.json +1 -1
  2. package/CHANGELOG.md +40 -0
  3. package/dist/cli/bundle.js +1345 -1003
  4. package/dist/cli/command-registry.js +5 -1
  5. package/dist/cli/commands/pipeline.d.ts +16 -0
  6. package/dist/cli/commands/pipeline.js +314 -0
  7. package/dist/cli/commands/ruvector-commands.js +17 -0
  8. package/dist/cli/commands/token-usage.js +24 -1
  9. package/dist/cli/handlers/heartbeat-handler.d.ts +26 -0
  10. package/dist/cli/handlers/heartbeat-handler.js +382 -0
  11. package/dist/cli/handlers/index.d.ts +2 -0
  12. package/dist/cli/handlers/index.js +2 -0
  13. package/dist/cli/handlers/routing-handler.d.ts +22 -0
  14. package/dist/cli/handlers/routing-handler.js +227 -0
  15. package/dist/cli/index.js +2 -0
  16. package/dist/coordination/deterministic-actions.d.ts +36 -0
  17. package/dist/coordination/deterministic-actions.js +257 -0
  18. package/dist/coordination/workflow-orchestrator.d.ts +18 -1
  19. package/dist/coordination/workflow-orchestrator.js +113 -3
  20. package/dist/coordination/workflow-types.d.ts +19 -1
  21. package/dist/coordination/workflow-types.js +3 -0
  22. package/dist/coordination/yaml-pipeline-loader.d.ts +1 -0
  23. package/dist/coordination/yaml-pipeline-loader.js +34 -0
  24. package/dist/domains/code-intelligence/coordinator-gnn.d.ts +21 -0
  25. package/dist/domains/code-intelligence/coordinator-gnn.js +102 -0
  26. package/dist/domains/contract-testing/coordinator.js +13 -0
  27. package/dist/domains/coverage-analysis/coordinator.js +5 -0
  28. package/dist/domains/defect-intelligence/coordinator.d.ts +1 -0
  29. package/dist/domains/defect-intelligence/coordinator.js +43 -0
  30. package/dist/domains/quality-assessment/coordinator.js +26 -0
  31. package/dist/domains/test-generation/coordinator.js +14 -0
  32. package/dist/integrations/agentic-flow/reasoning-bank/experience-replay.d.ts +11 -0
  33. package/dist/integrations/agentic-flow/reasoning-bank/experience-replay.js +44 -1
  34. package/dist/integrations/rl-suite/algorithms/eprop.d.ts +79 -0
  35. package/dist/integrations/rl-suite/algorithms/eprop.js +284 -0
  36. package/dist/integrations/rl-suite/algorithms/index.d.ts +2 -1
  37. package/dist/integrations/rl-suite/algorithms/index.js +2 -1
  38. package/dist/integrations/rl-suite/index.d.ts +2 -2
  39. package/dist/integrations/rl-suite/index.js +2 -2
  40. package/dist/integrations/rl-suite/interfaces.d.ts +3 -3
  41. package/dist/integrations/rl-suite/interfaces.js +1 -1
  42. package/dist/integrations/rl-suite/orchestrator.d.ts +2 -2
  43. package/dist/integrations/rl-suite/orchestrator.js +3 -2
  44. package/dist/integrations/rl-suite/reward-signals.d.ts +1 -1
  45. package/dist/integrations/rl-suite/reward-signals.js +1 -1
  46. package/dist/integrations/ruvector/coherence-gate-cohomology.d.ts +41 -0
  47. package/dist/integrations/ruvector/coherence-gate-cohomology.js +47 -0
  48. package/dist/integrations/ruvector/coherence-gate-core.d.ts +200 -0
  49. package/dist/integrations/ruvector/coherence-gate-core.js +294 -0
  50. package/dist/integrations/ruvector/coherence-gate-energy.d.ts +136 -0
  51. package/dist/integrations/ruvector/coherence-gate-energy.js +373 -0
  52. package/dist/integrations/ruvector/coherence-gate-vector.d.ts +38 -0
  53. package/dist/integrations/ruvector/coherence-gate-vector.js +76 -0
  54. package/dist/integrations/ruvector/coherence-gate.d.ts +10 -311
  55. package/dist/integrations/ruvector/coherence-gate.js +10 -652
  56. package/dist/integrations/ruvector/cold-tier-trainer.d.ts +103 -0
  57. package/dist/integrations/ruvector/cold-tier-trainer.js +377 -0
  58. package/dist/integrations/ruvector/cusum-detector.d.ts +70 -0
  59. package/dist/integrations/ruvector/cusum-detector.js +142 -0
  60. package/dist/integrations/ruvector/delta-tracker.d.ts +122 -0
  61. package/dist/integrations/ruvector/delta-tracker.js +311 -0
  62. package/dist/integrations/ruvector/domain-transfer.d.ts +79 -1
  63. package/dist/integrations/ruvector/domain-transfer.js +158 -2
  64. package/dist/integrations/ruvector/eprop-learner.d.ts +135 -0
  65. package/dist/integrations/ruvector/eprop-learner.js +351 -0
  66. package/dist/integrations/ruvector/feature-flags.d.ts +177 -0
  67. package/dist/integrations/ruvector/feature-flags.js +145 -0
  68. package/dist/integrations/ruvector/graphmae-encoder.d.ts +88 -0
  69. package/dist/integrations/ruvector/graphmae-encoder.js +360 -0
  70. package/dist/integrations/ruvector/hdc-fingerprint.d.ts +127 -0
  71. package/dist/integrations/ruvector/hdc-fingerprint.js +222 -0
  72. package/dist/integrations/ruvector/hopfield-memory.d.ts +97 -0
  73. package/dist/integrations/ruvector/hopfield-memory.js +238 -0
  74. package/dist/integrations/ruvector/index.d.ts +13 -2
  75. package/dist/integrations/ruvector/index.js +46 -2
  76. package/dist/integrations/ruvector/mincut-wrapper.d.ts +7 -0
  77. package/dist/integrations/ruvector/mincut-wrapper.js +54 -2
  78. package/dist/integrations/ruvector/reservoir-replay.d.ts +172 -0
  79. package/dist/integrations/ruvector/reservoir-replay.js +335 -0
  80. package/dist/integrations/ruvector/solver-adapter.d.ts +93 -0
  81. package/dist/integrations/ruvector/solver-adapter.js +299 -0
  82. package/dist/integrations/ruvector/sona-persistence.d.ts +33 -0
  83. package/dist/integrations/ruvector/sona-persistence.js +47 -0
  84. package/dist/integrations/ruvector/spectral-sparsifier.d.ts +154 -0
  85. package/dist/integrations/ruvector/spectral-sparsifier.js +389 -0
  86. package/dist/integrations/ruvector/temporal-causality.d.ts +63 -0
  87. package/dist/integrations/ruvector/temporal-causality.js +317 -0
  88. package/dist/learning/pattern-promotion.d.ts +63 -0
  89. package/dist/learning/pattern-promotion.js +235 -1
  90. package/dist/learning/pattern-store.d.ts +2 -0
  91. package/dist/learning/pattern-store.js +187 -1
  92. package/dist/learning/sqlite-persistence.d.ts +2 -0
  93. package/dist/learning/sqlite-persistence.js +4 -0
  94. package/dist/mcp/bundle.js +477 -380
  95. package/dist/mcp/handlers/heartbeat-handlers.d.ts +67 -0
  96. package/dist/mcp/handlers/heartbeat-handlers.js +180 -0
  97. package/dist/mcp/handlers/index.d.ts +2 -1
  98. package/dist/mcp/handlers/index.js +5 -1
  99. package/dist/mcp/handlers/task-handlers.d.ts +28 -0
  100. package/dist/mcp/handlers/task-handlers.js +39 -0
  101. package/dist/mcp/protocol-server.js +45 -1
  102. package/dist/mcp/server.js +41 -1
  103. package/dist/optimization/index.d.ts +2 -0
  104. package/dist/optimization/index.js +1 -0
  105. package/dist/optimization/session-cache.d.ts +80 -0
  106. package/dist/optimization/session-cache.js +227 -0
  107. package/dist/optimization/token-optimizer-service.d.ts +10 -0
  108. package/dist/optimization/token-optimizer-service.js +51 -0
  109. package/dist/routing/economic-routing.d.ts +126 -0
  110. package/dist/routing/economic-routing.js +290 -0
  111. package/dist/routing/index.d.ts +2 -0
  112. package/dist/routing/index.js +2 -0
  113. package/dist/routing/routing-feedback.d.ts +29 -0
  114. package/dist/routing/routing-feedback.js +75 -0
  115. package/dist/shared/utils/index.d.ts +1 -0
  116. package/dist/shared/utils/index.js +1 -0
  117. package/dist/shared/utils/xorshift128.d.ts +24 -0
  118. package/dist/shared/utils/xorshift128.js +50 -0
  119. package/package.json +1 -1
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Deterministic Step Actions (Imp-9)
3
+ *
4
+ * Built-in actions that execute WITHOUT LLM tokens. Each action maps to a
5
+ * domain + action pair and is automatically wired into the WorkflowOrchestrator
6
+ * as a fallback before domain service delegation.
7
+ *
8
+ * When the caller supplies explicit input values they are used directly.
9
+ * When inputs are omitted, the actions query the unified SQLite database
10
+ * for live metrics — keeping execution fully deterministic (SQL-only).
11
+ */
12
+ import { Result, DomainName } from '../shared/types/index.js';
13
+ import type { WorkflowContext } from './workflow-types.js';
14
+ /**
15
+ * A deterministic action that runs without any LLM tokens.
16
+ */
17
+ export interface DeterministicAction {
18
+ /** Unique action identifier */
19
+ id: string;
20
+ /** Target domain */
21
+ domain: DomainName;
22
+ /** Action name within the domain */
23
+ action: string;
24
+ /** Execute the action with the given input */
25
+ execute(input: Record<string, unknown>, context: WorkflowContext): Promise<Result<Record<string, unknown>, Error>>;
26
+ }
27
+ /**
28
+ * Look up a deterministic action by domain + action.
29
+ * Returns undefined if no built-in action matches.
30
+ */
31
+ export declare function findDeterministicAction(domain: DomainName, action: string): DeterministicAction | undefined;
32
+ /**
33
+ * Get all registered deterministic actions.
34
+ */
35
+ export declare function getAllDeterministicActions(): readonly DeterministicAction[];
36
+ //# sourceMappingURL=deterministic-actions.d.ts.map
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Deterministic Step Actions (Imp-9)
3
+ *
4
+ * Built-in actions that execute WITHOUT LLM tokens. Each action maps to a
5
+ * domain + action pair and is automatically wired into the WorkflowOrchestrator
6
+ * as a fallback before domain service delegation.
7
+ *
8
+ * When the caller supplies explicit input values they are used directly.
9
+ * When inputs are omitted, the actions query the unified SQLite database
10
+ * for live metrics — keeping execution fully deterministic (SQL-only).
11
+ */
12
+ import { ok } from '../shared/types/index.js';
13
+ function tryGetDb() {
14
+ try {
15
+ // Dynamic import to avoid hard dependency — the module may not be initialised
16
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
17
+ const { getUnifiedMemory } = require('../kernel/unified-memory.js');
18
+ const um = getUnifiedMemory();
19
+ if (!um.isInitialized())
20
+ return null;
21
+ return um.getDatabase();
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ // ============================================================================
28
+ // Quality Gate Check
29
+ // ============================================================================
30
+ const qualityGateCheck = {
31
+ id: 'quality-gate-check',
32
+ domain: 'quality-assessment',
33
+ action: 'gate-check',
34
+ async execute(input) {
35
+ const coverageMin = typeof input.coverageMin === 'number' ? input.coverageMin : 80;
36
+ const testsPassingMin = typeof input.testsPassingMin === 'number' ? input.testsPassingMin : 90;
37
+ const maxBugs = typeof input.maxBugs === 'number' ? input.maxBugs : 5;
38
+ // ----- Fetch live data from DB when not supplied by caller -----
39
+ let currentCoverage = typeof input.currentCoverage === 'number' ? input.currentCoverage : null;
40
+ let currentTestsPassingRate = typeof input.currentTestsPassingRate === 'number' ? input.currentTestsPassingRate : null;
41
+ let currentBugs = typeof input.currentBugs === 'number' ? input.currentBugs : null;
42
+ if (currentCoverage === null || currentTestsPassingRate === null || currentBugs === null) {
43
+ const db = tryGetDb();
44
+ if (db) {
45
+ try {
46
+ // Latest coverage from coverage_sessions
47
+ if (currentCoverage === null) {
48
+ const row = db.prepare(`SELECT after_lines FROM coverage_sessions ORDER BY created_at DESC LIMIT 1`).get();
49
+ currentCoverage = row?.after_lines ?? 0;
50
+ }
51
+ // Test pass rate from recent test_outcomes
52
+ if (currentTestsPassingRate === null) {
53
+ const row = db.prepare(`SELECT COUNT(*) as total,
54
+ SUM(CASE WHEN passed = 1 THEN 1 ELSE 0 END) as passed
55
+ FROM test_outcomes
56
+ WHERE created_at > datetime('now', '-7 days')`).get();
57
+ currentTestsPassingRate =
58
+ row && row.total > 0 ? (row.passed / row.total) * 100 : 0;
59
+ }
60
+ // Bug count: failed non-flaky tests in the last 7 days
61
+ if (currentBugs === null) {
62
+ const row = db.prepare(`SELECT COUNT(*) as bugs FROM test_outcomes
63
+ WHERE passed = 0 AND flaky = 0
64
+ AND created_at > datetime('now', '-7 days')`).get();
65
+ currentBugs = row?.bugs ?? 0;
66
+ }
67
+ }
68
+ catch {
69
+ // Graceful degradation — fall through to defaults
70
+ }
71
+ }
72
+ }
73
+ // Apply defaults for anything still null
74
+ currentCoverage = currentCoverage ?? 0;
75
+ currentTestsPassingRate = currentTestsPassingRate ?? 0;
76
+ currentBugs = currentBugs ?? 0;
77
+ const coveragePassed = currentCoverage >= coverageMin;
78
+ const testsPassed = currentTestsPassingRate >= testsPassingMin;
79
+ const bugsPassed = currentBugs <= maxBugs;
80
+ const passed = coveragePassed && testsPassed && bugsPassed;
81
+ // Score is a normalized 0-1 value
82
+ const coverageScore = Math.min(currentCoverage / coverageMin, 1);
83
+ const testsScore = Math.min(currentTestsPassingRate / testsPassingMin, 1);
84
+ const bugsScore = maxBugs > 0 ? Math.max(0, 1 - currentBugs / maxBugs) : (currentBugs === 0 ? 1 : 0);
85
+ const score = (coverageScore + testsScore + bugsScore) / 3;
86
+ return ok({
87
+ passed,
88
+ score: Math.round(score * 100) / 100,
89
+ source: typeof input.currentCoverage === 'number' ? 'input' : 'database',
90
+ details: {
91
+ coverage: { current: currentCoverage, threshold: coverageMin, passed: coveragePassed },
92
+ testsPassing: { current: currentTestsPassingRate, threshold: testsPassingMin, passed: testsPassed },
93
+ bugs: { current: currentBugs, threshold: maxBugs, passed: bugsPassed },
94
+ },
95
+ });
96
+ },
97
+ };
98
+ // ============================================================================
99
+ // Coverage Threshold Check
100
+ // ============================================================================
101
+ const coverageThresholdCheck = {
102
+ id: 'coverage-threshold',
103
+ domain: 'coverage-analysis',
104
+ action: 'threshold-check',
105
+ async execute(input) {
106
+ const minCoverage = typeof input.minCoverage === 'number' ? input.minCoverage : 80;
107
+ // ----- Fetch from DB when not supplied -----
108
+ let currentCoverage = typeof input.currentCoverage === 'number' ? input.currentCoverage : null;
109
+ let source = 'input';
110
+ if (currentCoverage === null) {
111
+ source = 'database';
112
+ const db = tryGetDb();
113
+ if (db) {
114
+ try {
115
+ const row = db.prepare(`SELECT after_lines FROM coverage_sessions ORDER BY created_at DESC LIMIT 1`).get();
116
+ currentCoverage = row?.after_lines ?? 0;
117
+ }
118
+ catch {
119
+ currentCoverage = 0;
120
+ }
121
+ }
122
+ else {
123
+ currentCoverage = 0;
124
+ }
125
+ }
126
+ const passed = currentCoverage >= minCoverage;
127
+ const gap = passed ? 0 : Math.round((minCoverage - currentCoverage) * 100) / 100;
128
+ return ok({
129
+ currentCoverage,
130
+ passed,
131
+ gap,
132
+ minCoverage,
133
+ source,
134
+ });
135
+ },
136
+ };
137
+ // ============================================================================
138
+ // Pattern Health Check
139
+ // ============================================================================
140
+ const patternHealthCheck = {
141
+ id: 'pattern-health',
142
+ domain: 'learning-optimization',
143
+ action: 'health-check',
144
+ async execute(input) {
145
+ // ----- Fetch from DB when not supplied -----
146
+ let totalPatterns = typeof input.totalPatterns === 'number' ? input.totalPatterns : null;
147
+ let activePatterns = typeof input.activePatterns === 'number' ? input.activePatterns : null;
148
+ let avgConfidence = typeof input.avgConfidence === 'number' ? input.avgConfidence : null;
149
+ let source = 'input';
150
+ if (totalPatterns === null || activePatterns === null || avgConfidence === null) {
151
+ source = 'database';
152
+ const db = tryGetDb();
153
+ if (db) {
154
+ try {
155
+ const row = db.prepare(`
156
+ SELECT COUNT(*) as total,
157
+ SUM(CASE WHEN deprecated_at IS NULL AND confidence >= 0.3 THEN 1 ELSE 0 END) as active,
158
+ AVG(confidence) as avg_conf
159
+ FROM qe_patterns
160
+ `).get();
161
+ totalPatterns = totalPatterns ?? (row?.total ?? 0);
162
+ activePatterns = activePatterns ?? (row?.active ?? 0);
163
+ avgConfidence = avgConfidence ?? (row?.avg_conf ?? 0);
164
+ }
165
+ catch {
166
+ // Graceful degradation
167
+ }
168
+ }
169
+ }
170
+ // Apply defaults for anything still null
171
+ totalPatterns = totalPatterns ?? 0;
172
+ activePatterns = activePatterns ?? 0;
173
+ avgConfidence = avgConfidence ?? 0;
174
+ // Health score: weighted combination of volume, activity ratio, and confidence
175
+ const volumeScore = Math.min(totalPatterns / 100, 1); // 100 patterns = max volume
176
+ const activityRatio = totalPatterns > 0 ? activePatterns / totalPatterns : 0;
177
+ const healthScore = Math.round((volumeScore * 0.3 + activityRatio * 0.3 + avgConfidence * 0.4) * 100) / 100;
178
+ return ok({
179
+ totalPatterns,
180
+ activePatterns,
181
+ avgConfidence,
182
+ healthScore,
183
+ source,
184
+ });
185
+ },
186
+ };
187
+ // ============================================================================
188
+ // Routing Accuracy Check
189
+ // ============================================================================
190
+ const routingAccuracyCheck = {
191
+ id: 'routing-accuracy',
192
+ domain: 'learning-optimization',
193
+ action: 'routing-check',
194
+ async execute(input) {
195
+ // ----- Fetch from DB when not supplied -----
196
+ let totalOutcomes = typeof input.totalOutcomes === 'number' ? input.totalOutcomes : null;
197
+ let successfulOutcomes = typeof input.successfulOutcomes === 'number' ? input.successfulOutcomes : null;
198
+ let confidenceCorrelation = typeof input.confidenceCorrelation === 'number' ? input.confidenceCorrelation : null;
199
+ let source = 'input';
200
+ if (totalOutcomes === null || successfulOutcomes === null) {
201
+ source = 'database';
202
+ const db = tryGetDb();
203
+ if (db) {
204
+ try {
205
+ const row = db.prepare(`
206
+ SELECT COUNT(*) as total,
207
+ SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successful
208
+ FROM routing_outcomes
209
+ `).get();
210
+ totalOutcomes = totalOutcomes ?? (row?.total ?? 0);
211
+ successfulOutcomes = successfulOutcomes ?? (row?.successful ?? 0);
212
+ }
213
+ catch {
214
+ // Graceful degradation
215
+ }
216
+ }
217
+ }
218
+ // Apply defaults
219
+ totalOutcomes = totalOutcomes ?? 0;
220
+ successfulOutcomes = successfulOutcomes ?? 0;
221
+ confidenceCorrelation = confidenceCorrelation ?? 0;
222
+ const successRate = totalOutcomes > 0
223
+ ? Math.round((successfulOutcomes / totalOutcomes) * 10000) / 100
224
+ : 0;
225
+ return ok({
226
+ successRate,
227
+ totalOutcomes,
228
+ successfulOutcomes,
229
+ confidenceCorrelation,
230
+ source,
231
+ });
232
+ },
233
+ };
234
+ // ============================================================================
235
+ // Registry
236
+ // ============================================================================
237
+ /** All built-in deterministic actions */
238
+ const DETERMINISTIC_ACTIONS = [
239
+ qualityGateCheck,
240
+ coverageThresholdCheck,
241
+ patternHealthCheck,
242
+ routingAccuracyCheck,
243
+ ];
244
+ /**
245
+ * Look up a deterministic action by domain + action.
246
+ * Returns undefined if no built-in action matches.
247
+ */
248
+ export function findDeterministicAction(domain, action) {
249
+ return DETERMINISTIC_ACTIONS.find((a) => a.domain === domain && a.action === action);
250
+ }
251
+ /**
252
+ * Get all registered deterministic actions.
253
+ */
254
+ export function getAllDeterministicActions() {
255
+ return DETERMINISTIC_ACTIONS;
256
+ }
257
+ //# sourceMappingURL=deterministic-actions.js.map
@@ -10,7 +10,7 @@
10
10
  import { Result, DomainName } from '../shared/types/index.js';
11
11
  import { EventBus, MemoryBackend, AgentCoordinator } from '../kernel/interfaces.js';
12
12
  import type { BehaviorNode } from './behavior-tree/nodes.js';
13
- export type { StepExecutionMode, StepStatus, WorkflowStatus, ConditionOperator, StepCondition, WorkflowStepDefinition, WorkflowDefinition, WorkflowTrigger, StepExecutionResult, WorkflowContext, WorkflowExecutionStatus, WorkflowListItem, WorkflowStartedPayload, WorkflowCompletedPayload, WorkflowFailedPayload, StepEventPayload, IWorkflowOrchestrator, DomainAction, DomainActionRegistry, WorkflowOrchestratorConfig, } from './workflow-types.js';
13
+ export type { StepExecutionMode, StepStatus, WorkflowStatus, ConditionOperator, StepCondition, WorkflowStepDefinition, WorkflowDefinition, WorkflowTrigger, StepExecutionResult, WorkflowContext, WorkflowExecutionStatus, WorkflowListItem, WorkflowStartedPayload, WorkflowCompletedPayload, WorkflowFailedPayload, StepEventPayload, StepAwaitingApprovalPayload, IWorkflowOrchestrator, DomainAction, DomainActionRegistry, WorkflowOrchestratorConfig, } from './workflow-types.js';
14
14
  export { WorkflowEvents, DEFAULT_WORKFLOW_CONFIG, } from './workflow-types.js';
15
15
  import type { WorkflowDefinition, WorkflowExecutionStatus, WorkflowListItem, IWorkflowOrchestrator, DomainAction, WorkflowOrchestratorConfig } from './workflow-types.js';
16
16
  export declare class WorkflowOrchestrator implements IWorkflowOrchestrator {
@@ -22,6 +22,8 @@ export declare class WorkflowOrchestrator implements IWorkflowOrchestrator {
22
22
  private readonly executions;
23
23
  private readonly actionRegistry;
24
24
  private readonly eventSubscriptions;
25
+ /** Pending approval gates: Map<`${executionId}:${stepId}`, gate> */
26
+ private readonly approvalGates;
25
27
  private initialized;
26
28
  constructor(eventBus: EventBus, memory: MemoryBackend, agentCoordinator: AgentCoordinator, config?: Partial<WorkflowOrchestratorConfig>);
27
29
  initialize(): Promise<void>;
@@ -36,6 +38,16 @@ export declare class WorkflowOrchestrator implements IWorkflowOrchestrator {
36
38
  listWorkflows(): WorkflowListItem[];
37
39
  getActiveExecutions(): WorkflowExecutionStatus[];
38
40
  getWorkflow(workflowId: string): WorkflowDefinition | undefined;
41
+ /**
42
+ * Approve a step that is awaiting approval. Returns true if the step
43
+ * was found and approved, false otherwise.
44
+ */
45
+ approveStep(executionId: string, stepId: string): boolean;
46
+ /**
47
+ * Reject a step that is awaiting approval. Returns true if the step
48
+ * was found and rejected, false otherwise.
49
+ */
50
+ rejectStep(executionId: string, stepId: string, reason?: string): boolean;
39
51
  isActionRegistered(domain: DomainName, action: string): boolean;
40
52
  getRegisteredActions(domain: DomainName): string[];
41
53
  getDomainsWithActions(): DomainName[];
@@ -81,6 +93,11 @@ export declare class WorkflowOrchestrator implements IWorkflowOrchestrator {
81
93
  private loadPersistedWorkflows;
82
94
  private persistWorkflows;
83
95
  private persistExecution;
96
+ /**
97
+ * Wait for external approval (or auto-approve after timeout).
98
+ * Returns an object with approved status and optional rejection reason.
99
+ */
100
+ private waitForApproval;
84
101
  private delay;
85
102
  }
86
103
  export declare function createWorkflowOrchestrator(eventBus: EventBus, memory: MemoryBackend, agentCoordinator: AgentCoordinator, config?: Partial<WorkflowOrchestratorConfig>): IWorkflowOrchestrator;
@@ -13,6 +13,8 @@ import { createEvent } from '../shared/events/domain-events.js';
13
13
  import { toError, toErrorMessage } from '../shared/error-utils.js';
14
14
  export { WorkflowEvents, DEFAULT_WORKFLOW_CONFIG, } from './workflow-types.js';
15
15
  import { WorkflowEvents, DEFAULT_WORKFLOW_CONFIG } from './workflow-types.js';
16
+ // Import deterministic actions
17
+ import { findDeterministicAction } from './deterministic-actions.js';
16
18
  // Import built-in workflows
17
19
  import { getBuiltInWorkflows, BUILTIN_WORKFLOW_IDS } from './workflow-builtin.js';
18
20
  // ============================================================================
@@ -27,6 +29,8 @@ export class WorkflowOrchestrator {
27
29
  executions = new Map();
28
30
  actionRegistry = {};
29
31
  eventSubscriptions = [];
32
+ /** Pending approval gates: Map<`${executionId}:${stepId}`, gate> */
33
+ approvalGates = new Map();
30
34
  initialized = false;
31
35
  constructor(eventBus, memory, agentCoordinator, config = {}) {
32
36
  this.eventBus = eventBus;
@@ -127,6 +131,13 @@ export class WorkflowOrchestrator {
127
131
  execution.status = 'cancelled';
128
132
  execution.completedAt = new Date();
129
133
  execution.duration = execution.completedAt.getTime() - execution.startedAt.getTime();
134
+ // Clean up any pending approval gates for this execution to prevent timer leaks
135
+ for (const [key, gate] of this.approvalGates.entries()) {
136
+ if (key.startsWith(`${executionId}:`)) {
137
+ gate.resolve({ approved: false, reason: 'Workflow cancelled' });
138
+ this.approvalGates.delete(key);
139
+ }
140
+ }
130
141
  await this.publishEvent(WorkflowEvents.WorkflowCancelled, {
131
142
  executionId, workflowId: execution.workflowId, workflowName: execution.workflowName,
132
143
  }, execution.context.metadata.correlationId);
@@ -171,6 +182,35 @@ export class WorkflowOrchestrator {
171
182
  return this.workflows.get(workflowId);
172
183
  }
173
184
  // ============================================================================
185
+ // Approval Gate Methods
186
+ // ============================================================================
187
+ /**
188
+ * Approve a step that is awaiting approval. Returns true if the step
189
+ * was found and approved, false otherwise.
190
+ */
191
+ approveStep(executionId, stepId) {
192
+ const key = `${executionId}:${stepId}`;
193
+ const gate = this.approvalGates.get(key);
194
+ if (!gate)
195
+ return false;
196
+ gate.resolve({ approved: true });
197
+ this.approvalGates.delete(key);
198
+ return true;
199
+ }
200
+ /**
201
+ * Reject a step that is awaiting approval. Returns true if the step
202
+ * was found and rejected, false otherwise.
203
+ */
204
+ rejectStep(executionId, stepId, reason) {
205
+ const key = `${executionId}:${stepId}`;
206
+ const gate = this.approvalGates.get(key);
207
+ if (!gate)
208
+ return false;
209
+ gate.resolve({ approved: false, reason });
210
+ this.approvalGates.delete(key);
211
+ return true;
212
+ }
213
+ // ============================================================================
174
214
  // Public Utility Methods
175
215
  // ============================================================================
176
216
  isActionRegistered(domain, action) {
@@ -402,6 +442,20 @@ export class WorkflowOrchestrator {
402
442
  const stepTimeout = step.timeout || this.config.defaultStepTimeout;
403
443
  const output = await this.executeStepAction(step, input, execution.context, stepTimeout);
404
444
  this.mapStepOutput(step, output, execution.context);
445
+ // Handle approval gate if configured
446
+ if (step.approval) {
447
+ const approvalResult = await this.waitForApproval(step, execution);
448
+ if (!approvalResult.approved) {
449
+ result.status = 'failed';
450
+ result.error = approvalResult.reason || 'Step rejected at approval gate';
451
+ result.output = output;
452
+ result.completedAt = new Date();
453
+ result.duration = result.completedAt.getTime() - startedAt.getTime();
454
+ execution.stepResults.set(step.id, result);
455
+ await this.publishStepFailed(execution, step, result.error);
456
+ return result;
457
+ }
458
+ }
405
459
  result.status = 'completed';
406
460
  result.output = output;
407
461
  result.completedAt = new Date();
@@ -439,11 +493,20 @@ export class WorkflowOrchestrator {
439
493
  }
440
494
  }
441
495
  async executeStepAction(step, input, context, timeout) {
496
+ const timeoutPromise = new Promise((_, reject) => {
497
+ setTimeout(() => reject(new Error(`Step timeout after ${timeout}ms`)), timeout);
498
+ });
499
+ // Check for a deterministic action first (zero LLM tokens)
500
+ const deterministicAction = findDeterministicAction(step.domain, step.action);
501
+ if (deterministicAction) {
502
+ const actionResult = await Promise.race([deterministicAction.execute(input, context), timeoutPromise]);
503
+ if (!actionResult.success)
504
+ throw actionResult.error;
505
+ return actionResult.value;
506
+ }
507
+ // Fall back to registered domain actions
442
508
  const domainActions = this.actionRegistry[step.domain];
443
509
  if (domainActions?.[step.action]) {
444
- const timeoutPromise = new Promise((_, reject) => {
445
- setTimeout(() => reject(new Error(`Step timeout after ${timeout}ms`)), timeout);
446
- });
447
510
  const actionResult = await Promise.race([domainActions[step.action](input, context), timeoutPromise]);
448
511
  if (!actionResult.success)
449
512
  throw actionResult.error;
@@ -751,6 +814,53 @@ export class WorkflowOrchestrator {
751
814
  }
752
815
  }
753
816
  // ============================================================================
817
+ // Private Methods - Approval Gates
818
+ // ============================================================================
819
+ /**
820
+ * Wait for external approval (or auto-approve after timeout).
821
+ * Returns an object with approved status and optional rejection reason.
822
+ */
823
+ async waitForApproval(step, execution) {
824
+ const approvalConfig = typeof step.approval === 'object' ? step.approval : {};
825
+ const autoApproveAfter = approvalConfig.autoApproveAfter ?? 300000; // 5 min default
826
+ const message = approvalConfig.message ?? `Awaiting approval for step: ${step.name}`;
827
+ // Update step result status
828
+ const stepResult = execution.stepResults.get(step.id);
829
+ if (stepResult)
830
+ stepResult.status = 'awaiting_approval';
831
+ // Emit StepAwaitingApproval event
832
+ await this.publishEvent(WorkflowEvents.StepAwaitingApproval, {
833
+ executionId: execution.executionId,
834
+ workflowId: execution.workflowId,
835
+ stepId: step.id,
836
+ stepName: step.name,
837
+ domain: step.domain,
838
+ message,
839
+ autoApproveAfter: autoApproveAfter > 0 ? autoApproveAfter : undefined,
840
+ }, execution.context.metadata.correlationId);
841
+ const key = `${execution.executionId}:${step.id}`;
842
+ return new Promise((resolve) => {
843
+ // Store the gate so approveStep/rejectStep can resolve it
844
+ const gate = { resolve };
845
+ this.approvalGates.set(key, gate);
846
+ // Auto-approve timer (0 = never auto-approve)
847
+ if (autoApproveAfter > 0) {
848
+ const timer = setTimeout(() => {
849
+ if (this.approvalGates.has(key)) {
850
+ this.approvalGates.delete(key);
851
+ resolve({ approved: true }); // auto-approve on timeout
852
+ }
853
+ }, autoApproveAfter);
854
+ // Wrap resolve to also clear the timer
855
+ const originalResolve = gate.resolve;
856
+ gate.resolve = (result) => {
857
+ clearTimeout(timer);
858
+ originalResolve(result);
859
+ };
860
+ }
861
+ });
862
+ }
863
+ // ============================================================================
754
864
  // Private Methods - Utilities
755
865
  // ============================================================================
756
866
  delay(ms) {
@@ -12,7 +12,7 @@ export type StepExecutionMode = 'sequential' | 'parallel';
12
12
  /**
13
13
  * Step status
14
14
  */
15
- export type StepStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
15
+ export type StepStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped' | 'awaiting_approval';
16
16
  /**
17
17
  * Workflow status
18
18
  */
@@ -70,6 +70,13 @@ export interface WorkflowStepDefinition {
70
70
  };
71
71
  /** Continue workflow on failure */
72
72
  continueOnFailure?: boolean;
73
+ /** Approval gate configuration */
74
+ approval?: boolean | {
75
+ /** Auto-approve after this many ms (0 = never auto-approve) */
76
+ autoApproveAfter?: number;
77
+ /** Approval prompt message */
78
+ message?: string;
79
+ };
73
80
  }
74
81
  /**
75
82
  * Workflow definition
@@ -178,6 +185,9 @@ export declare const WorkflowEvents: {
178
185
  readonly StepCompleted: "workflow.StepCompleted";
179
186
  readonly StepFailed: "workflow.StepFailed";
180
187
  readonly StepSkipped: "workflow.StepSkipped";
188
+ readonly StepAwaitingApproval: "workflow.StepAwaitingApproval";
189
+ readonly StepApproved: "workflow.StepApproved";
190
+ readonly StepRejected: "workflow.StepRejected";
181
191
  };
182
192
  export interface WorkflowStartedPayload {
183
193
  executionId: string;
@@ -207,6 +217,10 @@ export interface StepEventPayload {
207
217
  stepName: string;
208
218
  domain: DomainName;
209
219
  }
220
+ export interface StepAwaitingApprovalPayload extends StepEventPayload {
221
+ message?: string;
222
+ autoApproveAfter?: number;
223
+ }
210
224
  export interface IWorkflowOrchestrator {
211
225
  /** Initialize the orchestrator */
212
226
  initialize(): Promise<void>;
@@ -232,6 +246,10 @@ export interface IWorkflowOrchestrator {
232
246
  getActiveExecutions(): WorkflowExecutionStatus[];
233
247
  /** Get workflow definition */
234
248
  getWorkflow(workflowId: string): WorkflowDefinition | undefined;
249
+ /** Approve a step that is awaiting approval */
250
+ approveStep(executionId: string, stepId: string): boolean;
251
+ /** Reject a step that is awaiting approval */
252
+ rejectStep(executionId: string, stepId: string, reason?: string): boolean;
235
253
  }
236
254
  export type DomainAction = (input: Record<string, unknown>, context: WorkflowContext) => Promise<Result<unknown, Error>>;
237
255
  export interface DomainActionRegistry {
@@ -16,6 +16,9 @@ export const WorkflowEvents = {
16
16
  StepCompleted: 'workflow.StepCompleted',
17
17
  StepFailed: 'workflow.StepFailed',
18
18
  StepSkipped: 'workflow.StepSkipped',
19
+ StepAwaitingApproval: 'workflow.StepAwaitingApproval',
20
+ StepApproved: 'workflow.StepApproved',
21
+ StepRejected: 'workflow.StepRejected',
19
22
  };
20
23
  export const DEFAULT_WORKFLOW_CONFIG = {
21
24
  maxConcurrentWorkflows: 10,
@@ -22,6 +22,7 @@ export declare class YamlPipelineLoader {
22
22
  private validateTriggers;
23
23
  private validateRetry;
24
24
  private validateRollback;
25
+ private validateApproval;
25
26
  /** Validate that a value is Record<string, string>, undefined, or return an Error. */
26
27
  private validateStringRecord;
27
28
  /** Resolve a dot-path variable (e.g. "foo.bar") from a variables map. */
@@ -211,6 +211,14 @@ export class YamlPipelineLoader {
211
211
  const continueOnFailure = typeof s.continueOnFailure === 'boolean'
212
212
  ? s.continueOnFailure
213
213
  : undefined;
214
+ // Validate approval gate configuration
215
+ let approval;
216
+ if (s.approval !== undefined) {
217
+ const approvalResult = this.validateApproval(s.approval, s.id);
218
+ if (!approvalResult.success)
219
+ return approvalResult;
220
+ approval = approvalResult.value;
221
+ }
214
222
  const step = {
215
223
  id: s.id,
216
224
  name: s.name,
@@ -225,6 +233,7 @@ export class YamlPipelineLoader {
225
233
  ...(retry !== undefined && { retry }),
226
234
  ...(rollback !== undefined && { rollback }),
227
235
  ...(continueOnFailure !== undefined && { continueOnFailure }),
236
+ ...(approval !== undefined && { approval }),
228
237
  };
229
238
  steps.push(step);
230
239
  }
@@ -325,6 +334,31 @@ export class YamlPipelineLoader {
325
334
  ...(r.input !== undefined && typeof r.input === 'object' && { input: r.input }),
326
335
  });
327
336
  }
337
+ validateApproval(raw, stepId) {
338
+ // Simple boolean form: approval: true
339
+ if (typeof raw === 'boolean') {
340
+ return ok(raw);
341
+ }
342
+ // Object form: approval: { autoApproveAfter, message }
343
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
344
+ return err(new Error(`Step '${stepId}': 'approval' must be a boolean or object`));
345
+ }
346
+ const a = raw;
347
+ const result = {};
348
+ if (a.autoApproveAfter !== undefined) {
349
+ if (typeof a.autoApproveAfter !== 'number' || a.autoApproveAfter < 0) {
350
+ return err(new Error(`Step '${stepId}': approval.autoApproveAfter must be a non-negative number`));
351
+ }
352
+ result.autoApproveAfter = a.autoApproveAfter;
353
+ }
354
+ if (a.message !== undefined) {
355
+ if (typeof a.message !== 'string') {
356
+ return err(new Error(`Step '${stepId}': approval.message must be a string`));
357
+ }
358
+ result.message = a.message;
359
+ }
360
+ return ok(result);
361
+ }
328
362
  /** Validate that a value is Record<string, string>, undefined, or return an Error. */
329
363
  validateStringRecord(value, label) {
330
364
  if (value === undefined || value === null)
@@ -12,6 +12,13 @@ export declare function initializeGNNIndex(domainKey: string): QEGNNEmbeddingInd
12
12
  * Index code embeddings in GNN for fast similarity search
13
13
  */
14
14
  export declare function indexCodeEmbeddings(gnnIndex: QEGNNEmbeddingIndex, fileReader: FileReader, paths: string[]): Promise<void>;
15
+ /**
16
+ * R4: Generate graph-aware embeddings for a set of files using GraphMAE.
17
+ * Builds a dependency graph from import relationships and uses masked
18
+ * autoencoders to produce structure-aware embeddings.
19
+ * Falls back to feature-hash embeddings when GraphMAE is disabled.
20
+ */
21
+ export declare function generateGraphMAEEmbeddings(fileReader: FileReader, paths: string[]): Promise<Map<string, number[]>>;
15
22
  /**
16
23
  * Generate code embedding using semantic features
17
24
  */
@@ -34,6 +41,20 @@ export declare function mergeSearchResults(semanticResults: SearchResult[], gnnR
34
41
  file: string;
35
42
  similarity: number;
36
43
  }>): SearchResult[];
44
+ /**
45
+ * R6: Train GNN embeddings with memory-bounded cold-tier training.
46
+ * Uses LRU hotset caching when the graph exceeds hotsetSize, falling
47
+ * back to full in-memory training for small graphs.
48
+ */
49
+ export declare function trainWithColdTier(nodeFeatures: Map<number, Float32Array>, adjacency: Map<number, number[]>, options?: {
50
+ hotsetSize?: number;
51
+ epochs?: number;
52
+ hiddenDim?: number;
53
+ }): {
54
+ embeddings: Map<number, Float32Array>;
55
+ loss: number;
56
+ usedColdTier: boolean;
57
+ };
37
58
  /**
38
59
  * Simple hash function for strings
39
60
  */