@wundam/orchex 1.0.0-rc.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 (104) hide show
  1. package/LICENSE +65 -0
  2. package/README.md +332 -0
  3. package/bin/orchex.js +2 -0
  4. package/dist/artifacts.d.ts +132 -0
  5. package/dist/artifacts.js +832 -0
  6. package/dist/claude-executor.d.ts +31 -0
  7. package/dist/claude-executor.js +200 -0
  8. package/dist/commands.d.ts +36 -0
  9. package/dist/commands.js +264 -0
  10. package/dist/config.d.ts +100 -0
  11. package/dist/config.js +172 -0
  12. package/dist/context-builder.d.ts +46 -0
  13. package/dist/context-builder.js +506 -0
  14. package/dist/cost.d.ts +29 -0
  15. package/dist/cost.js +60 -0
  16. package/dist/execution-broadcaster.d.ts +18 -0
  17. package/dist/execution-broadcaster.js +17 -0
  18. package/dist/executors/base.d.ts +99 -0
  19. package/dist/executors/base.js +206 -0
  20. package/dist/executors/circuit-breaker.d.ts +36 -0
  21. package/dist/executors/circuit-breaker.js +109 -0
  22. package/dist/executors/deepseek-executor.d.ts +22 -0
  23. package/dist/executors/deepseek-executor.js +145 -0
  24. package/dist/executors/gemini-executor.d.ts +20 -0
  25. package/dist/executors/gemini-executor.js +176 -0
  26. package/dist/executors/index.d.ts +81 -0
  27. package/dist/executors/index.js +193 -0
  28. package/dist/executors/ollama-executor.d.ts +25 -0
  29. package/dist/executors/ollama-executor.js +184 -0
  30. package/dist/executors/openai-executor.d.ts +22 -0
  31. package/dist/executors/openai-executor.js +142 -0
  32. package/dist/index.d.ts +1 -0
  33. package/dist/index.js +115 -0
  34. package/dist/intelligence/anti-pattern-detector.d.ts +117 -0
  35. package/dist/intelligence/anti-pattern-detector.js +327 -0
  36. package/dist/intelligence/budget-enforcer.d.ts +119 -0
  37. package/dist/intelligence/budget-enforcer.js +226 -0
  38. package/dist/intelligence/context-optimizer.d.ts +111 -0
  39. package/dist/intelligence/context-optimizer.js +282 -0
  40. package/dist/intelligence/cost-tracker.d.ts +114 -0
  41. package/dist/intelligence/cost-tracker.js +183 -0
  42. package/dist/intelligence/deliverable-extractor.d.ts +134 -0
  43. package/dist/intelligence/deliverable-extractor.js +909 -0
  44. package/dist/intelligence/dependency-inferrer.d.ts +87 -0
  45. package/dist/intelligence/dependency-inferrer.js +403 -0
  46. package/dist/intelligence/diagnostics.d.ts +25 -0
  47. package/dist/intelligence/diagnostics.js +36 -0
  48. package/dist/intelligence/error-analyzer.d.ts +7 -0
  49. package/dist/intelligence/error-analyzer.js +76 -0
  50. package/dist/intelligence/file-chunker.d.ts +15 -0
  51. package/dist/intelligence/file-chunker.js +64 -0
  52. package/dist/intelligence/fix-stream-manager.d.ts +59 -0
  53. package/dist/intelligence/fix-stream-manager.js +212 -0
  54. package/dist/intelligence/heuristics.d.ts +23 -0
  55. package/dist/intelligence/heuristics.js +124 -0
  56. package/dist/intelligence/learning-engine.d.ts +157 -0
  57. package/dist/intelligence/learning-engine.js +433 -0
  58. package/dist/intelligence/learning-feedback.d.ts +96 -0
  59. package/dist/intelligence/learning-feedback.js +202 -0
  60. package/dist/intelligence/pattern-analyzer.d.ts +35 -0
  61. package/dist/intelligence/pattern-analyzer.js +189 -0
  62. package/dist/intelligence/plan-parser.d.ts +124 -0
  63. package/dist/intelligence/plan-parser.js +498 -0
  64. package/dist/intelligence/planner.d.ts +29 -0
  65. package/dist/intelligence/planner.js +86 -0
  66. package/dist/intelligence/self-healer.d.ts +16 -0
  67. package/dist/intelligence/self-healer.js +84 -0
  68. package/dist/intelligence/slicing-metrics.d.ts +62 -0
  69. package/dist/intelligence/slicing-metrics.js +202 -0
  70. package/dist/intelligence/slicing-templates.d.ts +81 -0
  71. package/dist/intelligence/slicing-templates.js +420 -0
  72. package/dist/intelligence/split-suggester.d.ts +69 -0
  73. package/dist/intelligence/split-suggester.js +176 -0
  74. package/dist/intelligence/stream-generator.d.ts +90 -0
  75. package/dist/intelligence/stream-generator.js +452 -0
  76. package/dist/logger.d.ts +34 -0
  77. package/dist/logger.js +83 -0
  78. package/dist/logging.d.ts +5 -0
  79. package/dist/logging.js +38 -0
  80. package/dist/manifest.d.ts +56 -0
  81. package/dist/manifest.js +254 -0
  82. package/dist/metrics.d.ts +35 -0
  83. package/dist/metrics.js +75 -0
  84. package/dist/orchestrator.d.ts +35 -0
  85. package/dist/orchestrator.js +723 -0
  86. package/dist/ownership.d.ts +44 -0
  87. package/dist/ownership.js +250 -0
  88. package/dist/semaphore.d.ts +12 -0
  89. package/dist/semaphore.js +34 -0
  90. package/dist/telemetry/telemetry-types.d.ts +85 -0
  91. package/dist/telemetry/telemetry-types.js +1 -0
  92. package/dist/tier-gating.d.ts +24 -0
  93. package/dist/tier-gating.js +88 -0
  94. package/dist/tiers.d.ts +92 -0
  95. package/dist/tiers.js +108 -0
  96. package/dist/tools.d.ts +18 -0
  97. package/dist/tools.js +1363 -0
  98. package/dist/types.d.ts +740 -0
  99. package/dist/types.js +160 -0
  100. package/dist/utils/ownership-validator.d.ts +6 -0
  101. package/dist/utils/ownership-validator.js +21 -0
  102. package/dist/waves.d.ts +21 -0
  103. package/dist/waves.js +146 -0
  104. package/package.json +120 -0
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Learning feedback module for adaptive heuristic adjustment.
3
+ * Analyzes execution history to improve slicing thresholds.
4
+ */
5
+ /**
6
+ * Default heuristic thresholds (conservative starting point).
7
+ */
8
+ export const DEFAULT_THRESHOLDS = {
9
+ maxOwnsCount: 4,
10
+ maxReadsCount: 4,
11
+ maxPlanAndCount: 3,
12
+ maxOutputTokens: 5000,
13
+ minSuccessRate: 0.7,
14
+ };
15
+ /**
16
+ * Minimum samples required before learning from a category.
17
+ */
18
+ export const MIN_CATEGORY_SAMPLES = 5;
19
+ /**
20
+ * Minimum total samples before suggesting adjustments.
21
+ */
22
+ export const MIN_TOTAL_SAMPLES = 20;
23
+ /**
24
+ * Learn from execution metrics and suggest threshold adjustments.
25
+ *
26
+ * @param metrics - Map of category to execution metrics
27
+ * @param currentThresholds - Current threshold values
28
+ * @param minSamples - Minimum samples required for learning
29
+ * @returns Array of suggested threshold adjustments
30
+ */
31
+ export function learnFromMetrics(metrics, currentThresholds = DEFAULT_THRESHOLDS, minSamples = MIN_TOTAL_SAMPLES) {
32
+ const adjustments = [];
33
+ // Aggregate all metrics
34
+ let totalExecutions = 0;
35
+ let totalSuccess = 0;
36
+ let weightedOwns = 0;
37
+ let weightedReads = 0;
38
+ let categoriesWithData = 0;
39
+ for (const m of metrics.values()) {
40
+ if (m.totalExecutions < MIN_CATEGORY_SAMPLES)
41
+ continue;
42
+ totalExecutions += m.totalExecutions;
43
+ totalSuccess += m.successCount;
44
+ weightedOwns += m.avgOwnsCount * m.totalExecutions;
45
+ weightedReads += m.avgReadsCount * m.totalExecutions;
46
+ categoriesWithData++;
47
+ }
48
+ if (totalExecutions < minSamples) {
49
+ return []; // Not enough data to learn from
50
+ }
51
+ const overallSuccessRate = totalSuccess / totalExecutions;
52
+ const avgOwns = weightedOwns / totalExecutions;
53
+ const avgReads = weightedReads / totalExecutions;
54
+ const confidence = totalExecutions >= 100 ? 'high' :
55
+ totalExecutions >= 50 ? 'medium' : 'low';
56
+ // Analyze high-owns categories with low success
57
+ const highOwnsLowSuccess = [...metrics.values()].filter(m => m.totalExecutions >= MIN_CATEGORY_SAMPLES &&
58
+ m.avgOwnsCount > currentThresholds.maxOwnsCount &&
59
+ m.successRate < 0.6);
60
+ if (highOwnsLowSuccess.length > 0) {
61
+ const newThreshold = Math.max(2, Math.floor(avgOwns * 0.8));
62
+ if (newThreshold !== currentThresholds.maxOwnsCount) {
63
+ adjustments.push({
64
+ field: 'maxOwnsCount',
65
+ oldValue: currentThresholds.maxOwnsCount,
66
+ newValue: newThreshold,
67
+ reason: `Streams with >${currentThresholds.maxOwnsCount} owns have <60% success rate`,
68
+ confidence,
69
+ });
70
+ }
71
+ }
72
+ // Analyze high-reads categories with high timeout rate
73
+ const highReadsHighTimeout = [...metrics.values()].filter(m => m.totalExecutions >= MIN_CATEGORY_SAMPLES &&
74
+ m.avgReadsCount > currentThresholds.maxReadsCount &&
75
+ (m.timeoutCount / m.totalExecutions) > 0.15);
76
+ if (highReadsHighTimeout.length > 0) {
77
+ const newThreshold = Math.max(2, Math.floor(avgReads * 0.7));
78
+ if (newThreshold !== currentThresholds.maxReadsCount) {
79
+ adjustments.push({
80
+ field: 'maxReadsCount',
81
+ oldValue: currentThresholds.maxReadsCount,
82
+ newValue: newThreshold,
83
+ reason: `Streams with >${currentThresholds.maxReadsCount} reads have >15% timeout rate`,
84
+ confidence,
85
+ });
86
+ }
87
+ }
88
+ // Adjust minSuccessRate if overall success is consistently high
89
+ if (overallSuccessRate > 0.85 && currentThresholds.minSuccessRate < 0.8) {
90
+ adjustments.push({
91
+ field: 'minSuccessRate',
92
+ oldValue: currentThresholds.minSuccessRate,
93
+ newValue: 0.8,
94
+ reason: `Overall success rate is ${(overallSuccessRate * 100).toFixed(0)}%, raising minimum threshold`,
95
+ confidence,
96
+ });
97
+ }
98
+ // Adjust minSuccessRate down if overall success is struggling
99
+ if (overallSuccessRate < 0.6 && currentThresholds.minSuccessRate > 0.6) {
100
+ adjustments.push({
101
+ field: 'minSuccessRate',
102
+ oldValue: currentThresholds.minSuccessRate,
103
+ newValue: 0.6,
104
+ reason: `Overall success rate is ${(overallSuccessRate * 100).toFixed(0)}%, lowering minimum threshold temporarily`,
105
+ confidence,
106
+ });
107
+ }
108
+ // Analyze high output token categories
109
+ const highTokensLowSuccess = [...metrics.values()].filter(m => m.totalExecutions >= MIN_CATEGORY_SAMPLES &&
110
+ m.avgOutputTokens > currentThresholds.maxOutputTokens &&
111
+ m.successRate < 0.7);
112
+ if (highTokensLowSuccess.length > 0) {
113
+ // Calculate average across high-token categories
114
+ const avgHighTokens = highTokensLowSuccess.reduce((sum, m) => sum + m.avgOutputTokens, 0) / highTokensLowSuccess.length;
115
+ const newThreshold = Math.max(3000, Math.floor(avgHighTokens * 0.8));
116
+ if (newThreshold !== currentThresholds.maxOutputTokens) {
117
+ adjustments.push({
118
+ field: 'maxOutputTokens',
119
+ oldValue: currentThresholds.maxOutputTokens,
120
+ newValue: newThreshold,
121
+ reason: `High-token streams averaging ${avgHighTokens.toFixed(0)} tokens have <70% success rate`,
122
+ confidence,
123
+ });
124
+ }
125
+ }
126
+ return adjustments;
127
+ }
128
+ /**
129
+ * Apply adjustments to thresholds with learning rate control.
130
+ *
131
+ * @param currentThresholds - Current threshold values
132
+ * @param adjustments - Suggested adjustments
133
+ * @param learningRate - How much to move toward suggested values (0-1)
134
+ * @returns Updated thresholds
135
+ */
136
+ export function applyAdjustments(currentThresholds, adjustments, learningRate = 0.3) {
137
+ const updated = { ...currentThresholds };
138
+ for (const adj of adjustments) {
139
+ const current = currentThresholds[adj.field];
140
+ const suggested = adj.newValue;
141
+ // Apply learning rate for gradual adaptation
142
+ const newValue = current * (1 - learningRate) + suggested * learningRate;
143
+ // Round appropriately based on field type
144
+ if (adj.field === 'minSuccessRate') {
145
+ updated[adj.field] = Math.round(newValue * 100) / 100; // 2 decimal places
146
+ }
147
+ else {
148
+ updated[adj.field] = Math.round(newValue);
149
+ }
150
+ }
151
+ return updated;
152
+ }
153
+ /**
154
+ * Format adjustments as a human-readable report.
155
+ */
156
+ export function formatAdjustmentsReport(adjustments) {
157
+ if (adjustments.length === 0) {
158
+ return 'No threshold adjustments suggested. Current thresholds are performing well.';
159
+ }
160
+ const lines = [
161
+ '=== Learning Feedback Report ===',
162
+ '',
163
+ `${adjustments.length} threshold adjustment${adjustments.length > 1 ? 's' : ''} suggested:`,
164
+ '',
165
+ ];
166
+ for (const adj of adjustments) {
167
+ const confidenceIcon = adj.confidence === 'high' ? '✅' : adj.confidence === 'medium' ? '⚠️' : '❓';
168
+ lines.push(`${confidenceIcon} ${adj.field}: ${adj.oldValue} → ${adj.newValue}`);
169
+ lines.push(` Reason: ${adj.reason}`);
170
+ lines.push(` Confidence: ${adj.confidence}`);
171
+ lines.push('');
172
+ }
173
+ return lines.join('\n');
174
+ }
175
+ /**
176
+ * Generate insights from category metrics.
177
+ */
178
+ export function generateCategoryInsights(metrics) {
179
+ const insights = [];
180
+ for (const [category, m] of metrics) {
181
+ if (m.totalExecutions < MIN_CATEGORY_SAMPLES)
182
+ continue;
183
+ // Low success rate warning
184
+ if (m.successRate < 0.6) {
185
+ insights.push(`${category} streams have ${(m.successRate * 100).toFixed(0)}% success rate. Consider reducing scope.`);
186
+ }
187
+ // High timeout rate warning
188
+ const timeoutRate = m.timeoutCount / m.totalExecutions;
189
+ if (timeoutRate > 0.15) {
190
+ insights.push(`${category} streams have ${(timeoutRate * 100).toFixed(0)}% timeout rate. Consider increasing timeout or reducing reads.`);
191
+ }
192
+ // High owns warning
193
+ if (m.avgOwnsCount > 4) {
194
+ insights.push(`${category} streams average ${m.avgOwnsCount.toFixed(1)} owned files. Consider splitting into smaller streams.`);
195
+ }
196
+ // High reads warning
197
+ if (m.avgReadsCount > 5) {
198
+ insights.push(`${category} streams average ${m.avgReadsCount.toFixed(1)} read files. High synthesis complexity may cause timeouts.`);
199
+ }
200
+ }
201
+ return insights;
202
+ }
@@ -0,0 +1,35 @@
1
+ import type { TelemetryAggregate } from '../telemetry/telemetry-types.js';
2
+ import type { ErrorCategory } from './error-analyzer.js';
3
+ export interface RetryPattern {
4
+ category: ErrorCategory;
5
+ totalOccurrences: number;
6
+ retrySuccessRate: number;
7
+ recommendation: 'always_retry' | 'conditional_retry' | 'no_retry';
8
+ }
9
+ export interface DurationPrediction {
10
+ complexityScore: number;
11
+ predictedDurationMs: number;
12
+ confidence: 'high' | 'medium' | 'low';
13
+ }
14
+ export interface WaveSizingRecommendation {
15
+ optimalParallelCount: number;
16
+ rationale: string;
17
+ }
18
+ export interface PatternAnalysis {
19
+ retryPatterns: RetryPattern[];
20
+ durationPredictions: DurationPrediction[];
21
+ waveSizing: WaveSizingRecommendation;
22
+ insights: string[];
23
+ }
24
+ /**
25
+ * Analyze telemetry aggregates to derive patterns.
26
+ * Requires at least 7 days of data for meaningful analysis.
27
+ */
28
+ export declare function analyzePatterns(aggregates: TelemetryAggregate[]): PatternAnalysis;
29
+ /**
30
+ * Get retry recommendation for a specific error category.
31
+ */
32
+ export declare function getRetryRecommendation(patterns: RetryPattern[], category: ErrorCategory): {
33
+ shouldRetry: boolean;
34
+ confidence: number;
35
+ };
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Analyze telemetry aggregates to derive patterns.
3
+ * Requires at least 7 days of data for meaningful analysis.
4
+ */
5
+ export function analyzePatterns(aggregates) {
6
+ const insights = [];
7
+ if (aggregates.length < 7) {
8
+ insights.push(`Insufficient data: ${aggregates.length} days. Need at least 7 days for pattern analysis.`);
9
+ return {
10
+ retryPatterns: [],
11
+ durationPredictions: [],
12
+ waveSizing: { optimalParallelCount: 5, rationale: 'Default (insufficient data)' },
13
+ insights,
14
+ };
15
+ }
16
+ // Aggregate totals across all periods
17
+ const totals = aggregates.reduce((acc, agg) => ({
18
+ orchestrationsTotal: acc.orchestrationsTotal + agg.orchestrationsTotal,
19
+ orchestrationsSuccess: acc.orchestrationsSuccess + agg.orchestrationsSuccess,
20
+ streamsTotal: acc.streamsTotal + agg.streamsTotal,
21
+ streamsSuccess: acc.streamsSuccess + agg.streamsSuccess,
22
+ selfHealTriggered: acc.selfHealTriggered + agg.selfHealTriggered,
23
+ selfHealSuccess: acc.selfHealSuccess + agg.selfHealSuccess,
24
+ errorsByCategory: {
25
+ timeout: acc.errorsByCategory.timeout + agg.errorsByCategory.timeout,
26
+ edit_mismatch: acc.errorsByCategory.edit_mismatch + agg.errorsByCategory.edit_mismatch,
27
+ invalid_artifact: acc.errorsByCategory.invalid_artifact + agg.errorsByCategory.invalid_artifact,
28
+ environment: acc.errorsByCategory.environment + agg.errorsByCategory.environment,
29
+ test_failure: acc.errorsByCategory.test_failure + agg.errorsByCategory.test_failure,
30
+ lint_error: acc.errorsByCategory.lint_error + agg.errorsByCategory.lint_error,
31
+ runtime_error: acc.errorsByCategory.runtime_error + agg.errorsByCategory.runtime_error,
32
+ unknown: acc.errorsByCategory.unknown + agg.errorsByCategory.unknown,
33
+ },
34
+ durationSum: acc.durationSum + (agg.avgDurationMs ?? 0),
35
+ durationCount: acc.durationCount + (agg.avgDurationMs ? 1 : 0),
36
+ }), {
37
+ orchestrationsTotal: 0, orchestrationsSuccess: 0, streamsTotal: 0, streamsSuccess: 0,
38
+ selfHealTriggered: 0, selfHealSuccess: 0,
39
+ errorsByCategory: {
40
+ timeout: 0, edit_mismatch: 0, invalid_artifact: 0, environment: 0,
41
+ test_failure: 0, lint_error: 0, runtime_error: 0, unknown: 0,
42
+ },
43
+ durationSum: 0,
44
+ durationCount: 0,
45
+ });
46
+ // Calculate retry patterns
47
+ const retryPatterns = [];
48
+ const selfHealRate = totals.selfHealTriggered > 0
49
+ ? totals.selfHealSuccess / totals.selfHealTriggered
50
+ : 0;
51
+ for (const [category, count] of Object.entries(totals.errorsByCategory)) {
52
+ if (count === 0)
53
+ continue;
54
+ // Estimate retry success rate based on error category characteristics
55
+ // These rates are derived from empirical observations and can be tuned
56
+ let retrySuccessRate;
57
+ let recommendation;
58
+ switch (category) {
59
+ case 'timeout':
60
+ retrySuccessRate = 0.7; // Timeouts often succeed on retry
61
+ recommendation = 'always_retry';
62
+ break;
63
+ case 'edit_mismatch':
64
+ retrySuccessRate = 0.9; // Almost always fixable with fresh context
65
+ recommendation = 'always_retry';
66
+ break;
67
+ case 'invalid_artifact':
68
+ retrySuccessRate = 0.6; // Often fixable with clearer instructions
69
+ recommendation = 'conditional_retry';
70
+ break;
71
+ case 'environment':
72
+ retrySuccessRate = 0.1; // Rarely fixable by retry
73
+ recommendation = 'no_retry';
74
+ break;
75
+ case 'test_failure':
76
+ // Self-healing helps significantly with test failures
77
+ retrySuccessRate = selfHealRate > 0.5 ? 0.7 : 0.5;
78
+ recommendation = 'conditional_retry';
79
+ break;
80
+ case 'lint_error':
81
+ retrySuccessRate = 0.8; // Usually fixable
82
+ recommendation = 'always_retry';
83
+ break;
84
+ case 'runtime_error':
85
+ retrySuccessRate = 0.4; // Depends on error type
86
+ recommendation = 'conditional_retry';
87
+ break;
88
+ default:
89
+ retrySuccessRate = 0.3;
90
+ recommendation = 'conditional_retry';
91
+ }
92
+ retryPatterns.push({ category, totalOccurrences: count, retrySuccessRate, recommendation });
93
+ }
94
+ // Calculate success rates
95
+ const successRate = totals.orchestrationsTotal > 0
96
+ ? totals.orchestrationsSuccess / totals.orchestrationsTotal
97
+ : 0;
98
+ const streamSuccessRate = totals.streamsTotal > 0
99
+ ? totals.streamsSuccess / totals.streamsTotal
100
+ : 0;
101
+ // Generate insights
102
+ insights.push(`Overall success rate: ${(successRate * 100).toFixed(1)}% (${totals.orchestrationsSuccess}/${totals.orchestrationsTotal})`);
103
+ insights.push(`Stream success rate: ${(streamSuccessRate * 100).toFixed(1)}% (${totals.streamsSuccess}/${totals.streamsTotal})`);
104
+ if (selfHealRate > 0) {
105
+ insights.push(`Self-healing success rate: ${(selfHealRate * 100).toFixed(1)}% (${totals.selfHealSuccess}/${totals.selfHealTriggered})`);
106
+ }
107
+ // Find most common error
108
+ const topError = Object.entries(totals.errorsByCategory)
109
+ .sort(([, a], [, b]) => b - a)[0];
110
+ if (topError && topError[1] > 0) {
111
+ insights.push(`Most common error: ${topError[0]} (${topError[1]} occurrences)`);
112
+ }
113
+ // Analyze trends
114
+ if (aggregates.length >= 14) {
115
+ const recentWeek = aggregates.slice(-7);
116
+ const previousWeek = aggregates.slice(-14, -7);
117
+ const recentSuccess = recentWeek.reduce((sum, a) => sum + a.orchestrationsSuccess, 0) /
118
+ Math.max(1, recentWeek.reduce((sum, a) => sum + a.orchestrationsTotal, 0));
119
+ const previousSuccess = previousWeek.reduce((sum, a) => sum + a.orchestrationsSuccess, 0) /
120
+ Math.max(1, previousWeek.reduce((sum, a) => sum + a.orchestrationsTotal, 0));
121
+ const trend = recentSuccess - previousSuccess;
122
+ if (Math.abs(trend) > 0.05) {
123
+ insights.push(`Trend: ${trend > 0 ? 'Improving' : 'Declining'} (${(trend * 100).toFixed(1)}% change week-over-week)`);
124
+ }
125
+ }
126
+ // Duration predictions based on actual data
127
+ const avgDuration = totals.durationCount > 0
128
+ ? totals.durationSum / totals.durationCount
129
+ : 60000; // Default 60s
130
+ const durationPredictions = [
131
+ {
132
+ complexityScore: 1,
133
+ predictedDurationMs: Math.round(avgDuration * 0.3),
134
+ confidence: totals.durationCount >= 10 ? 'medium' : 'low',
135
+ },
136
+ {
137
+ complexityScore: 5,
138
+ predictedDurationMs: Math.round(avgDuration),
139
+ confidence: totals.durationCount >= 10 ? 'high' : 'medium',
140
+ },
141
+ {
142
+ complexityScore: 10,
143
+ predictedDurationMs: Math.round(avgDuration * 2),
144
+ confidence: totals.durationCount >= 10 ? 'medium' : 'low',
145
+ },
146
+ ];
147
+ // Wave sizing recommendation based on success rate and error patterns
148
+ let optimalParallelCount = 5;
149
+ let rationale = '';
150
+ if (successRate > 0.85) {
151
+ optimalParallelCount = 10;
152
+ rationale = 'High success rate (>85%) supports aggressive parallelization';
153
+ }
154
+ else if (successRate > 0.7) {
155
+ optimalParallelCount = 7;
156
+ rationale = 'Good success rate (>70%) supports moderate parallelization';
157
+ }
158
+ else if (successRate > 0.5) {
159
+ optimalParallelCount = 5;
160
+ rationale = 'Moderate success rate (>50%) suggests conservative parallelization';
161
+ }
162
+ else {
163
+ optimalParallelCount = 3;
164
+ rationale = 'Low success rate (<50%) - recommend limited parallelization for easier debugging';
165
+ }
166
+ // Adjust for timeout patterns (high timeouts suggest reducing parallelism)
167
+ const timeoutRate = totals.orchestrationsTotal > 0
168
+ ? totals.errorsByCategory.timeout / totals.orchestrationsTotal
169
+ : 0;
170
+ if (timeoutRate > 0.2 && optimalParallelCount > 5) {
171
+ optimalParallelCount = Math.max(5, optimalParallelCount - 2);
172
+ rationale += '; reduced due to high timeout rate';
173
+ }
174
+ const waveSizing = { optimalParallelCount, rationale };
175
+ return { retryPatterns, durationPredictions, waveSizing, insights };
176
+ }
177
+ /**
178
+ * Get retry recommendation for a specific error category.
179
+ */
180
+ export function getRetryRecommendation(patterns, category) {
181
+ const pattern = patterns.find(p => p.category === category);
182
+ if (!pattern) {
183
+ return { shouldRetry: true, confidence: 0.5 }; // Default to retry with low confidence
184
+ }
185
+ return {
186
+ shouldRetry: pattern.recommendation !== 'no_retry',
187
+ confidence: pattern.retrySuccessRate,
188
+ };
189
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Markdown plan document parser.
3
+ * Extracts structure from planning documents for stream generation.
4
+ */
5
+ /**
6
+ * Code block extracted from markdown.
7
+ */
8
+ export interface CodeBlock {
9
+ /** Programming language (from fence) */
10
+ language: string;
11
+ /** Code content */
12
+ code: string;
13
+ /** Filename if specified in comment */
14
+ filename?: string;
15
+ }
16
+ /**
17
+ * Parsed section from markdown document.
18
+ */
19
+ export interface ParsedSection {
20
+ /** Header level (1=H1, 2=H2, etc.) */
21
+ level: number;
22
+ /** Section title */
23
+ title: string;
24
+ /** Section content (excluding children) */
25
+ content: string;
26
+ /** Code blocks in this section */
27
+ codeBlocks: CodeBlock[];
28
+ /** File references found in content */
29
+ fileReferences: string[];
30
+ /** Explicit dependencies mentioned in text */
31
+ explicitDeps: string[];
32
+ /** Child sections */
33
+ children: ParsedSection[];
34
+ }
35
+ /**
36
+ * Fully parsed planning document.
37
+ */
38
+ export interface ParsedPlan {
39
+ /** Document title (from first H1) */
40
+ title: string;
41
+ /** Brief description */
42
+ description: string;
43
+ /** Top-level sections */
44
+ sections: ParsedSection[];
45
+ /** All file references across document */
46
+ allFileReferences: string[];
47
+ /** All code blocks across document */
48
+ allCodeBlocks: CodeBlock[];
49
+ }
50
+ /**
51
+ * Extract file references from text.
52
+ * Matches patterns like: `src/foo.ts`, "src/bar.js", src/baz.ts
53
+ *
54
+ * Code blocks are stripped first to avoid extracting import paths
55
+ * (e.g., `from './foo.js'`) as real file references.
56
+ *
57
+ * Post-processing: `.js` files are dropped when a corresponding `.ts` exists,
58
+ * since TypeScript projects use .js in imports but .ts as source files.
59
+ */
60
+ export declare function extractFileReferences(text: string): string[];
61
+ /**
62
+ * Extract explicit dependency mentions from text.
63
+ * Matches patterns like: "depends on X", "requires Y", "after Z"
64
+ */
65
+ export declare function extractExplicitDeps(text: string): string[];
66
+ /**
67
+ * Extract code blocks from markdown.
68
+ * Only matches fences at column 0 (start of line) to avoid treating
69
+ * indented code examples inside YAML plan fields as separate blocks.
70
+ */
71
+ export declare function extractCodeBlocks(markdown: string): CodeBlock[];
72
+ /**
73
+ * Parse markdown into hierarchical sections.
74
+ */
75
+ export declare function parseMarkdownSections(markdown: string): ParsedSection[];
76
+ /**
77
+ * Parse a markdown planning document.
78
+ */
79
+ export declare function parsePlanDocument(markdown: string): ParsedPlan;
80
+ /**
81
+ * Get sections at a specific level (typically H2 for major deliverables).
82
+ */
83
+ export declare function getSectionsAtLevel(sections: ParsedSection[], level: number): ParsedSection[];
84
+ /**
85
+ * Flatten all sections into a single array.
86
+ */
87
+ export declare function flattenSections(sections: ParsedSection[]): ParsedSection[];
88
+ /**
89
+ * Parsed YAML stream definition from a code block.
90
+ */
91
+ export interface YamlStreamDefinition {
92
+ /** Stream ID */
93
+ id: string;
94
+ /** Human-readable name */
95
+ name: string;
96
+ /** Dependencies */
97
+ deps?: string[];
98
+ /** Owned files */
99
+ owns?: string[];
100
+ /** Read files */
101
+ reads?: string[];
102
+ /** Implementation plan */
103
+ plan?: string;
104
+ /** Verification commands */
105
+ verify?: string[];
106
+ /** Setup commands */
107
+ setup?: string[];
108
+ /** Timeout in milliseconds */
109
+ timeoutMs?: number;
110
+ }
111
+ /**
112
+ * Extract YAML stream definitions from code blocks in a section.
113
+ * Supports both flat format (one block per stream) and nested `streams:` wrapper format.
114
+ */
115
+ export declare function extractYamlStreamDefinitions(section: ParsedSection): YamlStreamDefinition[];
116
+ /**
117
+ * Find all sections that contain YAML stream definitions.
118
+ */
119
+ export declare function findYamlStreamSections(sections: ParsedSection[]): ParsedSection[];
120
+ /**
121
+ * Detect whether content is an unpopulated init-plan template.
122
+ * Checks for the ORCHEX PLAN TEMPLATE marker in HTML comments.
123
+ */
124
+ export declare function isUnpopulatedTemplate(content: string): boolean;