@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,44 @@
1
+ import type { FileOperation } from './types.js';
2
+ export interface OwnershipCheckOptions {
3
+ /**
4
+ * Allow common project files even if not in owns list.
5
+ * Default: true
6
+ */
7
+ allowCommonFiles?: boolean;
8
+ /**
9
+ * Include warnings in result for common files not in owns.
10
+ * Default: false
11
+ */
12
+ warnOnCommonFiles?: boolean;
13
+ /**
14
+ * Strict mode: disable all allowlists, only explicit owns allowed.
15
+ * Default: false
16
+ */
17
+ strict?: boolean;
18
+ }
19
+ export interface OwnershipCheckResult {
20
+ /** Files that violate ownership (will cause failure) */
21
+ violations: string[];
22
+ /** Files that are allowed but worth noting */
23
+ warnings: string[];
24
+ /** Files that passed ownership check */
25
+ allowed: string[];
26
+ }
27
+ /**
28
+ * Check whether all file operations fall within the stream's owns patterns.
29
+ *
30
+ * Security guarantees:
31
+ * - Path traversal is ALWAYS blocked (non-negotiable)
32
+ * - Absolute paths are ALWAYS blocked
33
+ * - Common files are only allowed in project root or owned directories
34
+ *
35
+ * @param operations - File operations to check
36
+ * @param owns - Ownership patterns from stream definition
37
+ * @param options - Checking options
38
+ */
39
+ export declare function checkOwnership(operations: FileOperation[], owns: string[], options?: OwnershipCheckOptions): OwnershipCheckResult;
40
+ /**
41
+ * Legacy function signature for backward compatibility.
42
+ * Returns only violations array (old behavior).
43
+ */
44
+ export declare function checkOwnershipLegacy(operations: FileOperation[], owns: string[]): string[];
@@ -0,0 +1,250 @@
1
+ import * as path from 'path';
2
+ // ============================================================================
3
+ // Common Allowed Files
4
+ // ============================================================================
5
+ /**
6
+ * Root-level files that are commonly created by scaffolds and are safe to allow.
7
+ * These are security-vetted and don't pose path traversal risks.
8
+ */
9
+ const COMMON_ALLOWED_FILES = new Set([
10
+ // Git
11
+ '.gitignore',
12
+ '.gitattributes',
13
+ '.gitkeep',
14
+ // Environment templates (never actual secrets)
15
+ '.env.example',
16
+ '.env.local.example',
17
+ '.env.development.example',
18
+ '.env.production.example',
19
+ '.env.test.example',
20
+ // Code formatting
21
+ '.prettierrc',
22
+ '.prettierrc.json',
23
+ '.prettierrc.js',
24
+ '.prettierrc.cjs',
25
+ '.prettierrc.mjs',
26
+ '.prettierrc.yaml',
27
+ '.prettierrc.yml',
28
+ '.prettierignore',
29
+ // Linting
30
+ '.eslintrc',
31
+ '.eslintrc.json',
32
+ '.eslintrc.js',
33
+ '.eslintrc.cjs',
34
+ '.eslintrc.mjs',
35
+ '.eslintrc.yaml',
36
+ '.eslintrc.yml',
37
+ '.eslintignore',
38
+ // Editor
39
+ '.editorconfig',
40
+ // Docker
41
+ '.dockerignore',
42
+ // Node version managers
43
+ '.nvmrc',
44
+ '.node-version',
45
+ '.tool-versions',
46
+ // TypeScript/JavaScript config (if not explicitly owned)
47
+ 'tsconfig.json',
48
+ 'jsconfig.json',
49
+ ]);
50
+ /**
51
+ * Regex patterns for files that are commonly created and safe to allow.
52
+ */
53
+ const COMMON_ALLOWED_PATTERNS = [
54
+ // Documentation
55
+ /^README\.md$/i,
56
+ /^CHANGELOG\.md$/i,
57
+ /^LICENSE(\.md|\.txt)?$/i,
58
+ /^CONTRIBUTING\.md$/i,
59
+ /^CODE_OF_CONDUCT\.md$/i,
60
+ /^SECURITY\.md$/i,
61
+ // GitHub
62
+ /^\.github\/.+/,
63
+ // VS Code
64
+ /^\.vscode\/.+/,
65
+ ];
66
+ // ============================================================================
67
+ // Core Functions
68
+ // ============================================================================
69
+ /**
70
+ * Check whether all file operations fall within the stream's owns patterns.
71
+ *
72
+ * Security guarantees:
73
+ * - Path traversal is ALWAYS blocked (non-negotiable)
74
+ * - Absolute paths are ALWAYS blocked
75
+ * - Common files are only allowed in project root or owned directories
76
+ *
77
+ * @param operations - File operations to check
78
+ * @param owns - Ownership patterns from stream definition
79
+ * @param options - Checking options
80
+ */
81
+ export function checkOwnership(operations, owns, options = {}) {
82
+ const { allowCommonFiles = true, warnOnCommonFiles = false, strict = false, } = options;
83
+ // Empty owns = no restriction (backward compatible)
84
+ if (owns.length === 0) {
85
+ return {
86
+ violations: [],
87
+ warnings: [],
88
+ allowed: operations.map((o) => o.path),
89
+ };
90
+ }
91
+ const violations = [];
92
+ const warnings = [];
93
+ const allowed = [];
94
+ for (const op of operations) {
95
+ const checkResult = checkSingleOperation(op, owns, {
96
+ allowCommonFiles: !strict && allowCommonFiles,
97
+ });
98
+ if (checkResult.violation) {
99
+ violations.push(checkResult.violation);
100
+ }
101
+ else {
102
+ allowed.push(op.path);
103
+ if (checkResult.warning && warnOnCommonFiles) {
104
+ warnings.push(checkResult.warning);
105
+ }
106
+ }
107
+ }
108
+ return { violations, warnings, allowed };
109
+ }
110
+ function checkSingleOperation(op, owns, options) {
111
+ const filePath = op.path;
112
+ // SECURITY: Block path traversal (non-negotiable, checked first)
113
+ if (filePath.includes('..')) {
114
+ return {
115
+ violation: `SECURITY: Path traversal blocked in '${filePath}'`,
116
+ };
117
+ }
118
+ // SECURITY: Block absolute paths
119
+ if (path.isAbsolute(filePath)) {
120
+ return {
121
+ violation: `SECURITY: Absolute path blocked: '${filePath}'`,
122
+ };
123
+ }
124
+ // SECURITY: Block hidden directories in parent paths (e.g., .ssh/config)
125
+ const parts = filePath.split('/');
126
+ for (let i = 0; i < parts.length - 1; i++) {
127
+ const part = parts[i];
128
+ if (part.startsWith('.') && !isAllowedHiddenDir(part)) {
129
+ return {
130
+ violation: `SECURITY: Hidden directory blocked: '${filePath}'`,
131
+ };
132
+ }
133
+ }
134
+ // Check explicit ownership
135
+ if (isExplicitlyOwned(filePath, owns)) {
136
+ return {};
137
+ }
138
+ // Check if it's a common allowed file
139
+ if (options.allowCommonFiles && isCommonAllowedFile(filePath, owns)) {
140
+ return {
141
+ warning: `Common file '${filePath}' created (not in owns list)`,
142
+ };
143
+ }
144
+ // Not allowed
145
+ return {
146
+ violation: `${op.type} on '${filePath}' is outside owned files`,
147
+ };
148
+ }
149
+ /**
150
+ * Check if a path is explicitly covered by an ownership pattern.
151
+ */
152
+ function isExplicitlyOwned(filePath, owns) {
153
+ return owns.some((pattern) => {
154
+ // Directory pattern: "src/" matches "src/foo.ts"
155
+ if (pattern.endsWith('/')) {
156
+ return filePath.startsWith(pattern);
157
+ }
158
+ // Glob pattern: "src/*.ts" matches "src/foo.ts"
159
+ if (pattern.includes('*')) {
160
+ return matchGlobPattern(pattern, filePath);
161
+ }
162
+ // Exact match
163
+ return filePath === pattern;
164
+ });
165
+ }
166
+ /**
167
+ * Check if a file is a common allowed file.
168
+ * Only allows files in:
169
+ * - Project root (for root-level common files)
170
+ * - Inside already owned directories
171
+ */
172
+ function isCommonAllowedFile(filePath, owns) {
173
+ const fileName = path.basename(filePath);
174
+ const dirName = path.dirname(filePath);
175
+ // Root-level common files
176
+ if (dirName === '.') {
177
+ if (COMMON_ALLOWED_FILES.has(fileName)) {
178
+ return true;
179
+ }
180
+ for (const pattern of COMMON_ALLOWED_PATTERNS) {
181
+ if (pattern.test(filePath)) {
182
+ return true;
183
+ }
184
+ }
185
+ }
186
+ // Pattern-matched files (like .github/workflows/ci.yml)
187
+ for (const pattern of COMMON_ALLOWED_PATTERNS) {
188
+ if (pattern.test(filePath)) {
189
+ return true;
190
+ }
191
+ }
192
+ // .gitkeep inside owned directories
193
+ if (fileName === '.gitkeep') {
194
+ const isInOwnedDir = owns.some((pattern) => {
195
+ if (pattern.endsWith('/')) {
196
+ return filePath.startsWith(pattern);
197
+ }
198
+ return false;
199
+ });
200
+ if (isInOwnedDir) {
201
+ return true;
202
+ }
203
+ }
204
+ return false;
205
+ }
206
+ /**
207
+ * Hidden directories that are allowed (dev tooling, not system files).
208
+ */
209
+ function isAllowedHiddenDir(dirName) {
210
+ const allowed = new Set([
211
+ '.github',
212
+ '.vscode',
213
+ '.idea',
214
+ '.husky',
215
+ '.storybook',
216
+ '.next',
217
+ '.nuxt',
218
+ '.svelte-kit',
219
+ '.turbo',
220
+ '.vercel',
221
+ '.netlify',
222
+ ]);
223
+ return allowed.has(dirName);
224
+ }
225
+ /**
226
+ * Match a simple glob pattern against a file path.
227
+ * Supports: * (any chars except /), ** (any chars including /)
228
+ */
229
+ function matchGlobPattern(pattern, filePath) {
230
+ // Escape regex special chars except * and **
231
+ let regexStr = pattern
232
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&')
233
+ .replace(/\*\*/g, '<<<GLOBSTAR>>>')
234
+ .replace(/\*/g, '[^/]*')
235
+ .replace(/<<<GLOBSTAR>>>/g, '.*');
236
+ return new RegExp(`^${regexStr}$`).test(filePath);
237
+ }
238
+ // ============================================================================
239
+ // Legacy Compatibility
240
+ // ============================================================================
241
+ /**
242
+ * Legacy function signature for backward compatibility.
243
+ * Returns only violations array (old behavior).
244
+ */
245
+ export function checkOwnershipLegacy(operations, owns) {
246
+ const result = checkOwnership(operations, owns, {
247
+ allowCommonFiles: false, // Old behavior: strict
248
+ });
249
+ return result.violations;
250
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Promise-based semaphore for limiting concurrent async operations.
3
+ * Used by ClaudeExecutor to cap parallel API calls.
4
+ */
5
+ export declare class Semaphore {
6
+ private readonly maxConcurrency;
7
+ private queue;
8
+ private running;
9
+ constructor(maxConcurrency: number);
10
+ acquire(): Promise<() => void>;
11
+ private release;
12
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Promise-based semaphore for limiting concurrent async operations.
3
+ * Used by ClaudeExecutor to cap parallel API calls.
4
+ */
5
+ export class Semaphore {
6
+ maxConcurrency;
7
+ queue = [];
8
+ running = 0;
9
+ constructor(maxConcurrency) {
10
+ this.maxConcurrency = maxConcurrency;
11
+ if (maxConcurrency < 1) {
12
+ throw new Error(`Semaphore concurrency must be >= 1, got ${maxConcurrency}`);
13
+ }
14
+ }
15
+ async acquire() {
16
+ if (this.running < this.maxConcurrency) {
17
+ this.running++;
18
+ return () => this.release();
19
+ }
20
+ return new Promise((resolve) => {
21
+ this.queue.push(() => {
22
+ this.running++;
23
+ resolve(() => this.release());
24
+ });
25
+ });
26
+ }
27
+ release() {
28
+ this.running--;
29
+ const next = this.queue.shift();
30
+ if (next) {
31
+ next();
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,85 @@
1
+ import type { ErrorCategory } from '../intelligence/error-analyzer.js';
2
+ import type { BudgetViolationType } from '../types.js';
3
+ export interface TelemetryEvent {
4
+ id: string;
5
+ sessionHash: string;
6
+ eventType: 'orchestration_start' | 'orchestration_complete' | 'stream_complete' | 'stream_failed' | 'self_heal_triggered';
7
+ timestamp: string;
8
+ durationMs?: number;
9
+ streamCount?: number;
10
+ waveCount?: number;
11
+ parallelCount?: number;
12
+ success?: boolean;
13
+ errorCategory?: ErrorCategory;
14
+ retryCount?: number;
15
+ selfHealCount?: number;
16
+ complexityScore?: number;
17
+ tokensInput?: number;
18
+ tokensOutput?: number;
19
+ provider?: string;
20
+ model?: string;
21
+ tier?: string;
22
+ /** Estimated context tokens before execution */
23
+ contextTokensEstimated?: number;
24
+ /** Actual context tokens from API response */
25
+ contextTokensActual?: number;
26
+ /** Budget utilization ratio (0-1) */
27
+ contextBudgetUtilization?: number;
28
+ /** Whether budget limits were exceeded */
29
+ budgetViolationType?: BudgetViolationType;
30
+ /** Provider's context window limit for this model */
31
+ providerContextLimit?: number;
32
+ /** Whether cache was hit */
33
+ cacheHit?: boolean;
34
+ /** Tokens read from cache */
35
+ cacheReadTokens?: number;
36
+ /** Tokens written to cache */
37
+ cacheWriteTokens?: number;
38
+ /** Milliseconds saved by parallel execution */
39
+ timeSavedMs?: number;
40
+ /** Hypothetical sequential execution time in ms */
41
+ sequentialMs?: number;
42
+ /** Actual parallel execution time in ms */
43
+ parallelMs?: number;
44
+ /** Stream name for category-based analysis */
45
+ streamName?: string;
46
+ }
47
+ export interface TelemetryAggregate {
48
+ period: string;
49
+ orchestrationsTotal: number;
50
+ orchestrationsSuccess: number;
51
+ orchestrationsFailed: number;
52
+ streamsTotal: number;
53
+ streamsSuccess: number;
54
+ streamsFailed: number;
55
+ avgDurationMs?: number;
56
+ p50DurationMs?: number;
57
+ p95DurationMs?: number;
58
+ errorsByCategory: Record<ErrorCategory, number>;
59
+ selfHealTriggered: number;
60
+ selfHealSuccess: number;
61
+ tokensInputTotal: number;
62
+ tokensOutputTotal: number;
63
+ /** Average context budget utilization (0-1) */
64
+ avgContextUtilization?: number;
65
+ /** Count of soft limit violations */
66
+ softViolationCount: number;
67
+ /** Count of hard limit violations */
68
+ hardViolationCount: number;
69
+ /** Streams that failed due to context exceeded */
70
+ contextExceededFailures: number;
71
+ /** Cache hit rate (0-1) */
72
+ cacheHitRate?: number;
73
+ /** Total tokens read from cache */
74
+ cacheReadTokensTotal: number;
75
+ /** Total tokens written to cache */
76
+ cacheWriteTokensTotal: number;
77
+ /** Estimated cost savings from cache */
78
+ estimatedCacheSavings?: number;
79
+ /** Total milliseconds saved by parallel execution */
80
+ timeSavedMsTotal: number;
81
+ /** Total hypothetical sequential time in ms */
82
+ sequentialMsTotal: number;
83
+ /** Total actual parallel time in ms */
84
+ parallelMsTotal: number;
85
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ import type { Tier } from './tiers.js';
2
+ import type { StreamDefinition } from './types.js';
3
+ export interface TierLimitCheckOptions {
4
+ tier: Tier;
5
+ streams: Record<string, StreamDefinition>;
6
+ projectDir: string;
7
+ mode: 'cloud' | 'local';
8
+ }
9
+ export interface TierLimitResult {
10
+ allowed: boolean;
11
+ error?: string;
12
+ code?: string;
13
+ suggestion?: string;
14
+ }
15
+ /**
16
+ * Checks tier gating limits for orchestrations: wave count, providers, and more.
17
+ * Returns { allowed, error, code, suggestion }
18
+ */
19
+ export declare function checkTierLimits(opts: TierLimitCheckOptions): TierLimitResult;
20
+ /**
21
+ * Generate a user-facing warning if wave count approaches or exceeds tier limit.
22
+ * Returns null if within limits. Used by `learn` tool to warn early.
23
+ */
24
+ export declare function getWaveCountWarning(waveCount: number, tier: Tier): string | null;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Checks tier gating limits for orchestrations: wave count, providers, and more.
3
+ * Returns { allowed, error, code, suggestion }
4
+ */
5
+ export function checkTierLimits(opts) {
6
+ const { tier, streams } = opts;
7
+ // 1. Wave count restriction
8
+ const depLevels = getMaxDependencyDepth(streams);
9
+ if (tier.maxWaves !== -1 && depLevels > tier.maxWaves) {
10
+ return {
11
+ allowed: false,
12
+ error: `Too many dependency waves for tier '${tier.name}'. Max allowed: ${tier.maxWaves}, required: ${depLevels}. Upgrade to increase this limit.`,
13
+ code: 'MAX_WAVES_EXCEEDED',
14
+ suggestion: `Split up your orchestration to use fewer dependency layers, or upgrade to Pro/Team for increased depth.`,
15
+ };
16
+ }
17
+ // 2. Maximum parallel agents (total stream count)
18
+ if (tier.maxParallelAgents !== -1) {
19
+ const totalStreams = Object.keys(streams).length;
20
+ if (totalStreams > tier.maxParallelAgents) {
21
+ return {
22
+ allowed: false,
23
+ error: `Too many streams for tier '${tier.name}'. Max allowed: ${tier.maxParallelAgents}, requested: ${totalStreams}. Upgrade to increase this limit.`,
24
+ code: 'MAX_AGENTS_EXCEEDED',
25
+ suggestion: `Reduce the number of streams to ${tier.maxParallelAgents} or fewer, or upgrade your tier.`,
26
+ };
27
+ }
28
+ }
29
+ // 3. maxProviders: Count distinct providers across streams with explicit provider field
30
+ if (tier.maxProviders !== -1) {
31
+ const providers = new Set();
32
+ for (const s of Object.values(streams)) {
33
+ if (s.provider)
34
+ providers.add(s.provider);
35
+ }
36
+ if (providers.size > tier.maxProviders) {
37
+ return {
38
+ allowed: false,
39
+ error: `Too many LLM providers for tier '${tier.name}'. Max allowed: ${tier.maxProviders}, requested: ${providers.size}. Upgrade to increase this limit.`,
40
+ code: 'MAX_PROVIDERS_EXCEEDED',
41
+ suggestion: `Reduce the number of distinct providers to ${tier.maxProviders} or fewer, or upgrade your tier.`,
42
+ };
43
+ }
44
+ }
45
+ return { allowed: true };
46
+ }
47
+ /**
48
+ * Generate a user-facing warning if wave count approaches or exceeds tier limit.
49
+ * Returns null if within limits. Used by `learn` tool to warn early.
50
+ */
51
+ export function getWaveCountWarning(waveCount, tier) {
52
+ if (tier.maxWaves === -1)
53
+ return null;
54
+ if (waveCount > tier.maxWaves) {
55
+ return `This plan requires ${waveCount} waves but your ${tier.name} tier allows max ${tier.maxWaves}. Reduce dependency depth or upgrade your tier.`;
56
+ }
57
+ if (waveCount >= Math.ceil(tier.maxWaves * 0.8)) {
58
+ return `This plan requires ${waveCount} waves, approaching your ${tier.name} tier limit of ${tier.maxWaves}.`;
59
+ }
60
+ return null;
61
+ }
62
+ // Utility: Returns maximum depth of dependencies (waves)
63
+ function getMaxDependencyDepth(streams) {
64
+ // For each stream, compute its max dependency chain length (DAG longest path)
65
+ const memo = {};
66
+ function dfs(id, visited) {
67
+ if (memo[id] !== undefined)
68
+ return memo[id];
69
+ if (visited.has(id))
70
+ return 0; // break cycles
71
+ visited.add(id);
72
+ const deps = streams[id]?.deps ?? [];
73
+ let max = 0;
74
+ for (const dep of deps) {
75
+ if (!streams[dep])
76
+ continue;
77
+ max = Math.max(max, dfs(dep, visited));
78
+ }
79
+ visited.delete(id);
80
+ memo[id] = max + 1;
81
+ return memo[id];
82
+ }
83
+ let result = 0;
84
+ for (const id of Object.keys(streams)) {
85
+ result = Math.max(result, dfs(id, new Set()));
86
+ }
87
+ return result;
88
+ }
@@ -0,0 +1,92 @@
1
+ import { z } from 'zod';
2
+ export declare const TierIdSchema: z.ZodEnum<["free", "pro", "team", "enterprise"]>;
3
+ export type TierId = z.infer<typeof TierIdSchema>;
4
+ export declare const TierSchema: z.ZodObject<{
5
+ id: z.ZodEnum<["free", "pro", "team", "enterprise"]>;
6
+ name: z.ZodString;
7
+ price: z.ZodNumber;
8
+ cloudOrchestrations: z.ZodNumber;
9
+ maxParallelAgents: z.ZodNumber;
10
+ selfHealing: z.ZodEnum<["none", "full"]>;
11
+ smartPlanning: z.ZodEnum<["none", "full"]>;
12
+ teamMembers: z.ZodNumber;
13
+ /** Maximum # of dependency waves (-1 = unlimited) */
14
+ maxWaves: z.ZodNumber;
15
+ /** Maximum # of distinct LLM providers per orchestration (-1 = unlimited) */
16
+ maxProviders: z.ZodNumber;
17
+ }, "strip", z.ZodTypeAny, {
18
+ name: string;
19
+ id: "free" | "pro" | "team" | "enterprise";
20
+ price: number;
21
+ cloudOrchestrations: number;
22
+ maxParallelAgents: number;
23
+ selfHealing: "none" | "full";
24
+ smartPlanning: "none" | "full";
25
+ teamMembers: number;
26
+ maxWaves: number;
27
+ maxProviders: number;
28
+ }, {
29
+ name: string;
30
+ id: "free" | "pro" | "team" | "enterprise";
31
+ price: number;
32
+ cloudOrchestrations: number;
33
+ maxParallelAgents: number;
34
+ selfHealing: "none" | "full";
35
+ smartPlanning: "none" | "full";
36
+ teamMembers: number;
37
+ maxWaves: number;
38
+ maxProviders: number;
39
+ }>;
40
+ export type Tier = z.infer<typeof TierSchema>;
41
+ export declare const TIERS: {
42
+ readonly free: {
43
+ readonly id: "free";
44
+ readonly name: "Free";
45
+ readonly price: 0;
46
+ readonly cloudOrchestrations: 0;
47
+ readonly maxParallelAgents: 5;
48
+ readonly selfHealing: "none";
49
+ readonly smartPlanning: "none";
50
+ readonly teamMembers: 1;
51
+ readonly maxWaves: 2;
52
+ readonly maxProviders: 1;
53
+ };
54
+ readonly pro: {
55
+ readonly id: "pro";
56
+ readonly name: "Pro";
57
+ readonly price: 19;
58
+ readonly cloudOrchestrations: 100;
59
+ readonly maxParallelAgents: 15;
60
+ readonly selfHealing: "full";
61
+ readonly smartPlanning: "full";
62
+ readonly teamMembers: 1;
63
+ readonly maxWaves: 10;
64
+ readonly maxProviders: 2;
65
+ };
66
+ readonly team: {
67
+ readonly id: "team";
68
+ readonly name: "Team";
69
+ readonly price: 49;
70
+ readonly cloudOrchestrations: 500;
71
+ readonly maxParallelAgents: 25;
72
+ readonly selfHealing: "full";
73
+ readonly smartPlanning: "full";
74
+ readonly teamMembers: -1;
75
+ readonly maxWaves: 25;
76
+ readonly maxProviders: 3;
77
+ };
78
+ readonly enterprise: {
79
+ readonly id: "enterprise";
80
+ readonly name: "Enterprise";
81
+ readonly price: -1;
82
+ readonly cloudOrchestrations: -1;
83
+ readonly maxParallelAgents: 50;
84
+ readonly selfHealing: "full";
85
+ readonly smartPlanning: "full";
86
+ readonly teamMembers: -1;
87
+ readonly maxWaves: -1;
88
+ readonly maxProviders: -1;
89
+ };
90
+ };
91
+ export declare function getTier(id: TierId): Tier;
92
+ export declare function isCloudTier(id: TierId): boolean;