@ai-sdlc/orchestrator 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters.d.ts +60 -0
- package/dist/adapters.js +160 -0
- package/dist/admission.d.ts +49 -0
- package/dist/admission.js +90 -0
- package/dist/analysis/analyzer.d.ts +10 -0
- package/dist/analysis/analyzer.js +67 -0
- package/dist/analysis/complexity-scorer.d.ts +24 -0
- package/dist/analysis/complexity-scorer.js +39 -0
- package/dist/analysis/context-builder.d.ts +14 -0
- package/dist/analysis/context-builder.js +79 -0
- package/dist/analysis/convention-detector.d.ts +9 -0
- package/dist/analysis/convention-detector.js +177 -0
- package/dist/analysis/dependency-parser.d.ts +17 -0
- package/dist/analysis/dependency-parser.js +167 -0
- package/dist/analysis/file-walker.d.ts +18 -0
- package/dist/analysis/file-walker.js +158 -0
- package/dist/analysis/hotspot-analyzer.d.ts +28 -0
- package/dist/analysis/hotspot-analyzer.js +85 -0
- package/dist/analysis/index.d.ts +13 -0
- package/dist/analysis/index.js +12 -0
- package/dist/analysis/pattern-detector.d.ts +10 -0
- package/dist/analysis/pattern-detector.js +86 -0
- package/dist/analysis/types.d.ts +94 -0
- package/dist/analysis/types.js +5 -0
- package/dist/audit-archival.d.ts +48 -0
- package/dist/audit-archival.js +72 -0
- package/dist/audit-export.d.ts +45 -0
- package/dist/audit-export.js +100 -0
- package/dist/audit-extended.d.ts +28 -0
- package/dist/audit-extended.js +38 -0
- package/dist/audit-scheduler.d.ts +32 -0
- package/dist/audit-scheduler.js +52 -0
- package/dist/audit-sqlite-sink.d.ts +11 -0
- package/dist/audit-sqlite-sink.js +51 -0
- package/dist/autonomy-tracker.d.ts +76 -0
- package/dist/autonomy-tracker.js +277 -0
- package/dist/check-runs.d.ts +37 -0
- package/dist/check-runs.js +72 -0
- package/dist/cli/commands/agents.d.ts +6 -0
- package/dist/cli/commands/agents.js +33 -0
- package/dist/cli/commands/complexity.d.ts +6 -0
- package/dist/cli/commands/complexity.js +35 -0
- package/dist/cli/commands/cost.d.ts +6 -0
- package/dist/cli/commands/cost.js +53 -0
- package/dist/cli/commands/dashboard.d.ts +9 -0
- package/dist/cli/commands/dashboard.js +50 -0
- package/dist/cli/commands/health.d.ts +6 -0
- package/dist/cli/commands/health.js +33 -0
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.js +127 -0
- package/dist/cli/commands/routing.d.ts +6 -0
- package/dist/cli/commands/routing.js +46 -0
- package/dist/cli/commands/run.d.ts +6 -0
- package/dist/cli/commands/run.js +36 -0
- package/dist/cli/commands/start.d.ts +6 -0
- package/dist/cli/commands/start.js +26 -0
- package/dist/cli/commands/status.d.ts +6 -0
- package/dist/cli/commands/status.js +34 -0
- package/dist/cli/dashboard-renderer.d.ts +24 -0
- package/dist/cli/dashboard-renderer.js +129 -0
- package/dist/cli/formatters/index.d.ts +6 -0
- package/dist/cli/formatters/index.js +18 -0
- package/dist/cli/formatters/json.d.ts +5 -0
- package/dist/cli/formatters/json.js +7 -0
- package/dist/cli/formatters/minimal.d.ts +5 -0
- package/dist/cli/formatters/minimal.js +36 -0
- package/dist/cli/formatters/table.d.ts +5 -0
- package/dist/cli/formatters/table.js +184 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +35 -0
- package/dist/compliance-extended.d.ts +41 -0
- package/dist/compliance-extended.js +58 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.js +66 -0
- package/dist/context-enrichment.d.ts +33 -0
- package/dist/context-enrichment.js +125 -0
- package/dist/cost-governance.d.ts +22 -0
- package/dist/cost-governance.js +106 -0
- package/dist/cost-tracker.d.ts +75 -0
- package/dist/cost-tracker.js +177 -0
- package/dist/defaults.d.ts +140 -0
- package/dist/defaults.js +218 -0
- package/dist/deploy/flyio-target.d.ts +19 -0
- package/dist/deploy/flyio-target.js +114 -0
- package/dist/deploy/index.d.ts +15 -0
- package/dist/deploy/index.js +11 -0
- package/dist/deploy/kubernetes-target.d.ts +25 -0
- package/dist/deploy/kubernetes-target.js +124 -0
- package/dist/deploy/metrics-collector.d.ts +31 -0
- package/dist/deploy/metrics-collector.js +75 -0
- package/dist/deploy/rollout-controller.d.ts +42 -0
- package/dist/deploy/rollout-controller.js +192 -0
- package/dist/deploy/rollout-types.d.ts +86 -0
- package/dist/deploy/rollout-types.js +5 -0
- package/dist/deploy/types.d.ts +47 -0
- package/dist/deploy/types.js +5 -0
- package/dist/deploy/vercel-target.d.ts +16 -0
- package/dist/deploy/vercel-target.js +115 -0
- package/dist/discovery.d.ts +26 -0
- package/dist/discovery.js +46 -0
- package/dist/episodic-enhanced.d.ts +88 -0
- package/dist/episodic-enhanced.js +212 -0
- package/dist/execute.d.ts +81 -0
- package/dist/execute.js +703 -0
- package/dist/fix-ci.d.ts +61 -0
- package/dist/fix-ci.js +380 -0
- package/dist/handoff-executor.d.ts +61 -0
- package/dist/handoff-executor.js +119 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +87 -0
- package/dist/instrumented.d.ts +27 -0
- package/dist/instrumented.js +34 -0
- package/dist/logger.d.ts +17 -0
- package/dist/logger.js +37 -0
- package/dist/multi-repo/impact-analyzer.d.ts +22 -0
- package/dist/multi-repo/impact-analyzer.js +95 -0
- package/dist/multi-repo/index.d.ts +5 -0
- package/dist/multi-repo/index.js +4 -0
- package/dist/multi-repo/monorepo-detector.d.ts +16 -0
- package/dist/multi-repo/monorepo-detector.js +246 -0
- package/dist/multi-repo/service-map-builder.d.ts +24 -0
- package/dist/multi-repo/service-map-builder.js +235 -0
- package/dist/multi-repo/types.d.ts +54 -0
- package/dist/multi-repo/types.js +5 -0
- package/dist/notifications/index.d.ts +4 -0
- package/dist/notifications/index.js +4 -0
- package/dist/notifications/notification-router.d.ts +62 -0
- package/dist/notifications/notification-router.js +206 -0
- package/dist/notifications/slack-messenger.d.ts +26 -0
- package/dist/notifications/slack-messenger.js +69 -0
- package/dist/notifications/teams-messenger.d.ts +21 -0
- package/dist/notifications/teams-messenger.js +85 -0
- package/dist/notifications.d.ts +13 -0
- package/dist/notifications.js +15 -0
- package/dist/orchestration.d.ts +20 -0
- package/dist/orchestration.js +53 -0
- package/dist/orchestrator.d.ts +160 -0
- package/dist/orchestrator.js +372 -0
- package/dist/otel-exporter.d.ts +37 -0
- package/dist/otel-exporter.js +128 -0
- package/dist/plugin.d.ts +45 -0
- package/dist/plugin.js +8 -0
- package/dist/policy-evaluators.d.ts +40 -0
- package/dist/policy-evaluators.js +57 -0
- package/dist/process-escalation.d.ts +38 -0
- package/dist/process-escalation.js +129 -0
- package/dist/progressive-gates.d.ts +54 -0
- package/dist/progressive-gates.js +135 -0
- package/dist/provenance.d.ts +29 -0
- package/dist/provenance.js +61 -0
- package/dist/reconcilers.d.ts +28 -0
- package/dist/reconcilers.js +37 -0
- package/dist/runners/claude-code.d.ts +17 -0
- package/dist/runners/claude-code.js +200 -0
- package/dist/runners/codex.d.ts +19 -0
- package/dist/runners/codex.js +153 -0
- package/dist/runners/copilot-stub.d.ts +9 -0
- package/dist/runners/copilot-stub.js +15 -0
- package/dist/runners/copilot.d.ts +15 -0
- package/dist/runners/copilot.js +119 -0
- package/dist/runners/cursor-stub.d.ts +9 -0
- package/dist/runners/cursor-stub.js +15 -0
- package/dist/runners/cursor.d.ts +20 -0
- package/dist/runners/cursor.js +140 -0
- package/dist/runners/devin-stub.d.ts +9 -0
- package/dist/runners/devin-stub.js +15 -0
- package/dist/runners/generic-llm.d.ts +51 -0
- package/dist/runners/generic-llm.js +125 -0
- package/dist/runners/index.d.ts +8 -0
- package/dist/runners/index.js +7 -0
- package/dist/runners/runner-registry.d.ts +50 -0
- package/dist/runners/runner-registry.js +156 -0
- package/dist/runners/types.d.ts +59 -0
- package/dist/runners/types.js +7 -0
- package/dist/security.d.ts +51 -0
- package/dist/security.js +70 -0
- package/dist/shared.d.ts +134 -0
- package/dist/shared.js +284 -0
- package/dist/state/index.d.ts +4 -0
- package/dist/state/index.js +3 -0
- package/dist/state/schema.d.ts +16 -0
- package/dist/state/schema.js +264 -0
- package/dist/state/store.d.ts +109 -0
- package/dist/state/store.js +644 -0
- package/dist/state/types.d.ts +190 -0
- package/dist/state/types.js +5 -0
- package/dist/structured-logger.d.ts +20 -0
- package/dist/structured-logger.js +64 -0
- package/dist/task-decomposer.d.ts +61 -0
- package/dist/task-decomposer.js +265 -0
- package/dist/telemetry-extended.d.ts +24 -0
- package/dist/telemetry-extended.js +31 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.js +7 -0
- package/dist/validate-agent-output.d.ts +30 -0
- package/dist/validate-agent-output.js +81 -0
- package/dist/validate-issue.d.ts +25 -0
- package/dist/validate-issue.js +102 -0
- package/dist/watch.d.ts +43 -0
- package/dist/watch.js +99 -0
- package/dist/webhook-manager.d.ts +38 -0
- package/dist/webhook-manager.js +66 -0
- package/package.json +59 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrator class — central entry point wrapping executePipeline,
|
|
3
|
+
* startWatch, executeFixCI, and the state store.
|
|
4
|
+
*/
|
|
5
|
+
import { executePipeline } from './execute.js';
|
|
6
|
+
import { startWatch } from './watch.js';
|
|
7
|
+
import { executeFixCI } from './fix-ci.js';
|
|
8
|
+
import { loadConfig } from './config.js';
|
|
9
|
+
import { StateStore } from './state/index.js';
|
|
10
|
+
import { createLogger } from './logger.js';
|
|
11
|
+
import { analyzeCodebase } from './analysis/analyzer.js';
|
|
12
|
+
import { buildCodebaseContext } from './analysis/context-builder.js';
|
|
13
|
+
import { DEFAULT_ANALYSIS_CACHE_TTL_MS } from './defaults.js';
|
|
14
|
+
import { AutonomyTracker } from './autonomy-tracker.js';
|
|
15
|
+
import { CostTracker } from './cost-tracker.js';
|
|
16
|
+
import { CostGovernancePlugin } from './cost-governance.js';
|
|
17
|
+
export class Orchestrator {
|
|
18
|
+
config;
|
|
19
|
+
_state;
|
|
20
|
+
log;
|
|
21
|
+
_autonomyTracker;
|
|
22
|
+
_costTracker;
|
|
23
|
+
plugins;
|
|
24
|
+
_pluginsInitialized;
|
|
25
|
+
constructor(config) {
|
|
26
|
+
this.config = config;
|
|
27
|
+
this.log = config.logger ?? createLogger();
|
|
28
|
+
this.plugins = config.plugins ?? [];
|
|
29
|
+
if (config.statePath) {
|
|
30
|
+
this._state = StateStore.open(config.statePath);
|
|
31
|
+
this._autonomyTracker = new AutonomyTracker(this._state);
|
|
32
|
+
this._costTracker = new CostTracker(this._state);
|
|
33
|
+
}
|
|
34
|
+
// Initialize plugins with shared context
|
|
35
|
+
this._pluginsInitialized = this._initPlugins();
|
|
36
|
+
}
|
|
37
|
+
async _initPlugins() {
|
|
38
|
+
const ctx = {
|
|
39
|
+
store: this._state,
|
|
40
|
+
costTracker: this._costTracker,
|
|
41
|
+
autonomyTracker: this._autonomyTracker,
|
|
42
|
+
log: this.log,
|
|
43
|
+
};
|
|
44
|
+
for (const plugin of this.plugins) {
|
|
45
|
+
await plugin.initialize?.(ctx);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Run the full pipeline for a single issue.
|
|
50
|
+
*/
|
|
51
|
+
async run(issueNumber, overrides) {
|
|
52
|
+
const runId = `run-${Date.now()}-${issueNumber}`;
|
|
53
|
+
const startedAt = new Date().toISOString();
|
|
54
|
+
// Record pipeline start in state store
|
|
55
|
+
if (this._state) {
|
|
56
|
+
this._state.savePipelineRun({
|
|
57
|
+
runId,
|
|
58
|
+
issueNumber,
|
|
59
|
+
pipelineType: 'execute',
|
|
60
|
+
status: 'running',
|
|
61
|
+
currentStage: 'init',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// Ensure plugins are initialized before running
|
|
65
|
+
await this._pluginsInitialized;
|
|
66
|
+
// Auto-register cost governance plugin if costPolicy is present and not already registered
|
|
67
|
+
try {
|
|
68
|
+
const configDir = this.config.configDir ?? `${this.config.workDir ?? '.'}/.ai-sdlc`;
|
|
69
|
+
const earlyConfig = loadConfig(configDir);
|
|
70
|
+
if (earlyConfig.pipeline?.spec.costPolicy &&
|
|
71
|
+
!this.plugins.some((p) => p.name === 'cost-governance')) {
|
|
72
|
+
const costPlugin = new CostGovernancePlugin(earlyConfig.pipeline.spec.costPolicy);
|
|
73
|
+
await costPlugin.initialize({
|
|
74
|
+
store: this._state,
|
|
75
|
+
costTracker: this._costTracker,
|
|
76
|
+
autonomyTracker: this._autonomyTracker,
|
|
77
|
+
log: this.log,
|
|
78
|
+
});
|
|
79
|
+
this.plugins.push(costPlugin);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Config load may fail here; executePipeline will report it properly
|
|
84
|
+
}
|
|
85
|
+
// Notify plugins before run
|
|
86
|
+
for (const plugin of this.plugins) {
|
|
87
|
+
await plugin.beforeRun?.({ runId, issueNumber, startedAt });
|
|
88
|
+
}
|
|
89
|
+
const runStart = Date.now();
|
|
90
|
+
try {
|
|
91
|
+
const result = await executePipeline(issueNumber, {
|
|
92
|
+
configDir: this.config.configDir,
|
|
93
|
+
workDir: this.config.workDir,
|
|
94
|
+
runner: this.config.runner,
|
|
95
|
+
security: this.config.security,
|
|
96
|
+
logger: this.log,
|
|
97
|
+
autonomyTracker: this._autonomyTracker,
|
|
98
|
+
costTracker: this._costTracker,
|
|
99
|
+
stateStore: this._state,
|
|
100
|
+
...overrides,
|
|
101
|
+
});
|
|
102
|
+
// Record success in state store
|
|
103
|
+
if (this._state) {
|
|
104
|
+
this._state.updatePipelineRunStatus(runId, 'completed', {
|
|
105
|
+
result: JSON.stringify({
|
|
106
|
+
prUrl: result.prUrl,
|
|
107
|
+
filesChanged: result.filesChanged.length,
|
|
108
|
+
promotionEligible: result.promotionEligible,
|
|
109
|
+
}),
|
|
110
|
+
});
|
|
111
|
+
this._state.saveEpisodicRecord({
|
|
112
|
+
issueNumber,
|
|
113
|
+
pipelineType: 'execute',
|
|
114
|
+
outcome: 'success',
|
|
115
|
+
filesChanged: result.filesChanged.length,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
// Notify plugins after successful run
|
|
119
|
+
const durationMs = Date.now() - runStart;
|
|
120
|
+
for (const plugin of this.plugins) {
|
|
121
|
+
await plugin.afterRun?.({ runId, issueNumber, result, durationMs });
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
// Record failure in state store
|
|
127
|
+
if (this._state) {
|
|
128
|
+
this._state.updatePipelineRunStatus(runId, 'failed', {
|
|
129
|
+
result: JSON.stringify({
|
|
130
|
+
error: err instanceof Error ? err.message : String(err),
|
|
131
|
+
}),
|
|
132
|
+
});
|
|
133
|
+
this._state.saveEpisodicRecord({
|
|
134
|
+
issueNumber,
|
|
135
|
+
pipelineType: 'execute',
|
|
136
|
+
outcome: 'failure',
|
|
137
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
// Notify plugins on error
|
|
141
|
+
const errorDurationMs = Date.now() - runStart;
|
|
142
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
143
|
+
for (const plugin of this.plugins) {
|
|
144
|
+
await plugin.onError?.({ runId, issueNumber, error, durationMs: errorDurationMs });
|
|
145
|
+
}
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Start watch mode — continuous reconciliation loop.
|
|
151
|
+
*/
|
|
152
|
+
async start(options) {
|
|
153
|
+
return startWatch({
|
|
154
|
+
executeOptions: {
|
|
155
|
+
runner: this.config.runner,
|
|
156
|
+
security: this.config.security,
|
|
157
|
+
logger: this.log,
|
|
158
|
+
},
|
|
159
|
+
...options,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Fix a failing CI run on an agent-created PR.
|
|
164
|
+
*/
|
|
165
|
+
async fixCI(prNumber, runId, overrides) {
|
|
166
|
+
return executeFixCI(prNumber, runId, {
|
|
167
|
+
configDir: this.config.configDir,
|
|
168
|
+
workDir: this.config.workDir,
|
|
169
|
+
runner: this.config.runner,
|
|
170
|
+
security: this.config.security,
|
|
171
|
+
logger: this.log,
|
|
172
|
+
...overrides,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get pipeline status, optionally filtered by issue number.
|
|
177
|
+
*/
|
|
178
|
+
async status(issueNumber) {
|
|
179
|
+
const configDir = this.config.configDir ?? `${this.config.workDir ?? '.'}/.ai-sdlc`;
|
|
180
|
+
const config = loadConfig(configDir);
|
|
181
|
+
const recentRuns = this._state
|
|
182
|
+
? this._state.getPipelineRuns(issueNumber, 10).map((r) => ({
|
|
183
|
+
runId: r.runId,
|
|
184
|
+
issueNumber: r.issueNumber,
|
|
185
|
+
status: r.status,
|
|
186
|
+
startedAt: r.startedAt,
|
|
187
|
+
}))
|
|
188
|
+
: [];
|
|
189
|
+
return { config, recentRuns };
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Health check — validates config, state store, and adapter connectivity.
|
|
193
|
+
*/
|
|
194
|
+
async health() {
|
|
195
|
+
const errors = [];
|
|
196
|
+
let configValid = false;
|
|
197
|
+
const stateStoreConnected = !!this._state;
|
|
198
|
+
try {
|
|
199
|
+
const configDir = this.config.configDir ?? `${this.config.workDir ?? '.'}/.ai-sdlc`;
|
|
200
|
+
loadConfig(configDir);
|
|
201
|
+
configValid = true;
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
errors.push(`Config: ${err instanceof Error ? err.message : String(err)}`);
|
|
205
|
+
}
|
|
206
|
+
return { configValid, stateStoreConnected, errors };
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Analyze the codebase and return a CodebaseProfile.
|
|
210
|
+
* Caches the result in the state store if available.
|
|
211
|
+
*/
|
|
212
|
+
async analyze(options) {
|
|
213
|
+
const workDir = this.config.workDir ?? process.cwd();
|
|
214
|
+
// Check cache (24h) unless forced
|
|
215
|
+
if (!options?.force && this._state) {
|
|
216
|
+
const cached = this._state.getLatestComplexityProfile(workDir);
|
|
217
|
+
if (cached?.analyzedAt) {
|
|
218
|
+
const age = Date.now() - new Date(cached.analyzedAt).getTime();
|
|
219
|
+
if (age < DEFAULT_ANALYSIS_CACHE_TTL_MS && cached.architecturalPatterns) {
|
|
220
|
+
// Return reconstructed profile from cached data
|
|
221
|
+
return {
|
|
222
|
+
repoPath: cached.repoPath,
|
|
223
|
+
score: cached.score,
|
|
224
|
+
filesCount: cached.filesCount ?? 0,
|
|
225
|
+
modulesCount: cached.modulesCount ?? 0,
|
|
226
|
+
dependencyCount: cached.dependencyCount ?? 0,
|
|
227
|
+
modules: [],
|
|
228
|
+
moduleGraph: cached.moduleGraph
|
|
229
|
+
? JSON.parse(cached.moduleGraph)
|
|
230
|
+
: { modules: [], edges: [], externalDependencies: [], cycles: [] },
|
|
231
|
+
architecturalPatterns: cached.architecturalPatterns
|
|
232
|
+
? JSON.parse(cached.architecturalPatterns)
|
|
233
|
+
: [],
|
|
234
|
+
hotspots: cached.hotspots ? JSON.parse(cached.hotspots) : [],
|
|
235
|
+
conventions: cached.conventionsData ? JSON.parse(cached.conventionsData) : [],
|
|
236
|
+
analyzedAt: cached.analyzedAt,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const profile = await analyzeCodebase({ repoPath: workDir });
|
|
242
|
+
// Persist to state store
|
|
243
|
+
if (this._state) {
|
|
244
|
+
this._state.saveCodebaseProfile({
|
|
245
|
+
repoPath: profile.repoPath,
|
|
246
|
+
score: profile.score,
|
|
247
|
+
filesCount: profile.filesCount,
|
|
248
|
+
modulesCount: profile.modulesCount,
|
|
249
|
+
dependencyCount: profile.dependencyCount,
|
|
250
|
+
architecturalPatterns: JSON.stringify(profile.architecturalPatterns),
|
|
251
|
+
hotspots: JSON.stringify(profile.hotspots),
|
|
252
|
+
moduleGraph: JSON.stringify(profile.moduleGraph),
|
|
253
|
+
conventionsData: JSON.stringify(profile.conventions),
|
|
254
|
+
});
|
|
255
|
+
// Save individual hotspot records
|
|
256
|
+
for (const hotspot of profile.hotspots) {
|
|
257
|
+
this._state.saveHotspot({
|
|
258
|
+
repoPath: workDir,
|
|
259
|
+
filePath: hotspot.filePath,
|
|
260
|
+
churnRate: hotspot.churnRate,
|
|
261
|
+
complexity: hotspot.complexity,
|
|
262
|
+
commitCount: hotspot.commitCount,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return profile;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Get agent roster with autonomy levels and performance.
|
|
270
|
+
*/
|
|
271
|
+
async agents() {
|
|
272
|
+
if (!this._state)
|
|
273
|
+
return [];
|
|
274
|
+
return this._state.getAllAutonomyLedgerEntries();
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Get routing decision history.
|
|
278
|
+
*/
|
|
279
|
+
async routing(options) {
|
|
280
|
+
if (!this._state)
|
|
281
|
+
return [];
|
|
282
|
+
return this._state.getRoutingHistory(options?.limit ?? 50);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Get codebase complexity profile as CodebaseContext.
|
|
286
|
+
*/
|
|
287
|
+
async complexity(options) {
|
|
288
|
+
const profile = await this.analyze({ force: options?.analyze });
|
|
289
|
+
const context = buildCodebaseContext(profile);
|
|
290
|
+
return { profile, context };
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Get cost summary and budget status.
|
|
294
|
+
*/
|
|
295
|
+
async cost(opts) {
|
|
296
|
+
if (!this._costTracker) {
|
|
297
|
+
return {
|
|
298
|
+
summary: {
|
|
299
|
+
totalCostUsd: 0,
|
|
300
|
+
totalTokens: 0,
|
|
301
|
+
totalInputTokens: 0,
|
|
302
|
+
totalOutputTokens: 0,
|
|
303
|
+
entryCount: 0,
|
|
304
|
+
avgCostPerRun: 0,
|
|
305
|
+
avgTokensPerRun: 0,
|
|
306
|
+
costByAgent: {},
|
|
307
|
+
costByModel: {},
|
|
308
|
+
},
|
|
309
|
+
budget: {
|
|
310
|
+
budgetUsd: 0,
|
|
311
|
+
spentUsd: 0,
|
|
312
|
+
remainingUsd: 0,
|
|
313
|
+
utilizationPercent: 0,
|
|
314
|
+
overBudget: false,
|
|
315
|
+
projectedMonthlyUsd: 0,
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
return {
|
|
320
|
+
summary: this._costTracker.getCostSummary(opts?.since),
|
|
321
|
+
budget: this._costTracker.getBudgetStatus(opts?.budget, opts?.since),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Get dashboard data snapshot for TUI rendering.
|
|
326
|
+
*/
|
|
327
|
+
async dashboard() {
|
|
328
|
+
const runs = this._state
|
|
329
|
+
? this._state.getPipelineRuns(undefined, 10).map((r) => ({
|
|
330
|
+
runId: r.runId,
|
|
331
|
+
status: r.status,
|
|
332
|
+
startedAt: r.startedAt,
|
|
333
|
+
}))
|
|
334
|
+
: [];
|
|
335
|
+
const agents = this._state ? this._state.getAllAutonomyLedgerEntries() : [];
|
|
336
|
+
const costData = await this.cost();
|
|
337
|
+
return {
|
|
338
|
+
runs,
|
|
339
|
+
agents,
|
|
340
|
+
costSummary: costData.summary,
|
|
341
|
+
budgetStatus: costData.budget,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Access the autonomy tracker.
|
|
346
|
+
*/
|
|
347
|
+
get autonomyTracker() {
|
|
348
|
+
return this._autonomyTracker;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Access the cost tracker.
|
|
352
|
+
*/
|
|
353
|
+
get costTracker() {
|
|
354
|
+
return this._costTracker;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Access the state store directly.
|
|
358
|
+
*/
|
|
359
|
+
get state() {
|
|
360
|
+
return this._state;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Clean up resources. Calls shutdown() on all plugins.
|
|
364
|
+
*/
|
|
365
|
+
async close() {
|
|
366
|
+
for (const plugin of this.plugins) {
|
|
367
|
+
await plugin.shutdown?.();
|
|
368
|
+
}
|
|
369
|
+
this._state?.close();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
//# sourceMappingURL=orchestrator.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTel bridge — wraps MetricStore to forward metrics to OpenTelemetry SDK
|
|
3
|
+
* when OTEL_EXPORTER_OTLP_ENDPOINT is set. No-op otherwise.
|
|
4
|
+
*
|
|
5
|
+
* D5: Only activates when the env var is set. Preserves existing no-op behavior.
|
|
6
|
+
* RFC reference: Lines 891-908 (OTel export).
|
|
7
|
+
*/
|
|
8
|
+
import { type MetricStore } from '@ai-sdlc/reference';
|
|
9
|
+
export interface OTelBridgeOptions {
|
|
10
|
+
/** Override the endpoint check (for testing). */
|
|
11
|
+
forceEnable?: boolean;
|
|
12
|
+
/** Custom service name. */
|
|
13
|
+
serviceName?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface OTelBridge extends MetricStore {
|
|
16
|
+
/** Whether the bridge is actively forwarding to OTel. */
|
|
17
|
+
readonly enabled: boolean;
|
|
18
|
+
/** Create a pipeline run span with proper parent/child hierarchy. */
|
|
19
|
+
startPipelineSpan(runId: string, pipelineType: string): OTelSpanHandle;
|
|
20
|
+
/** Create a child span for a pipeline stage. */
|
|
21
|
+
startStageSpan(parentRunId: string, stageName: string): OTelSpanHandle;
|
|
22
|
+
}
|
|
23
|
+
export interface OTelSpanHandle {
|
|
24
|
+
end(status?: 'ok' | 'error'): void;
|
|
25
|
+
setAttribute(key: string, value: string | number): void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create an OTel bridge that wraps a MetricStore.
|
|
29
|
+
* When OTEL_EXPORTER_OTLP_ENDPOINT is set, metrics are forwarded to OTel.
|
|
30
|
+
* Otherwise, it delegates all calls to the underlying store with no overhead.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createOTelBridge(metricStore: MetricStore, options?: OTelBridgeOptions): OTelBridge;
|
|
33
|
+
/**
|
|
34
|
+
* Check if OTel export is available (endpoint configured).
|
|
35
|
+
*/
|
|
36
|
+
export declare function isOTelAvailable(): boolean;
|
|
37
|
+
//# sourceMappingURL=otel-exporter.d.ts.map
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTel bridge — wraps MetricStore to forward metrics to OpenTelemetry SDK
|
|
3
|
+
* when OTEL_EXPORTER_OTLP_ENDPOINT is set. No-op otherwise.
|
|
4
|
+
*
|
|
5
|
+
* D5: Only activates when the env var is set. Preserves existing no-op behavior.
|
|
6
|
+
* RFC reference: Lines 891-908 (OTel export).
|
|
7
|
+
*/
|
|
8
|
+
import { ATTRIBUTE_KEYS, getMeter, } from '@ai-sdlc/reference';
|
|
9
|
+
/**
|
|
10
|
+
* Create an OTel bridge that wraps a MetricStore.
|
|
11
|
+
* When OTEL_EXPORTER_OTLP_ENDPOINT is set, metrics are forwarded to OTel.
|
|
12
|
+
* Otherwise, it delegates all calls to the underlying store with no overhead.
|
|
13
|
+
*/
|
|
14
|
+
export function createOTelBridge(metricStore, options) {
|
|
15
|
+
const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
16
|
+
const enabled = options?.forceEnable ?? !!endpoint;
|
|
17
|
+
const meter = getMeter();
|
|
18
|
+
// OTel counters/histograms (created lazily, no-op if OTel SDK not installed)
|
|
19
|
+
const counters = new Map();
|
|
20
|
+
const histograms = new Map();
|
|
21
|
+
function getCounter(name) {
|
|
22
|
+
let c = counters.get(name);
|
|
23
|
+
if (!c) {
|
|
24
|
+
c = meter.createCounter(name);
|
|
25
|
+
counters.set(name, c);
|
|
26
|
+
}
|
|
27
|
+
return c;
|
|
28
|
+
}
|
|
29
|
+
function getHistogram(name) {
|
|
30
|
+
let h = histograms.get(name);
|
|
31
|
+
if (!h) {
|
|
32
|
+
h = meter.createHistogram(name);
|
|
33
|
+
histograms.set(name, h);
|
|
34
|
+
}
|
|
35
|
+
return h;
|
|
36
|
+
}
|
|
37
|
+
function forwardToOTel(point) {
|
|
38
|
+
if (!enabled)
|
|
39
|
+
return;
|
|
40
|
+
const { metric, value, labels } = point;
|
|
41
|
+
// Determine if counter or histogram based on metric name
|
|
42
|
+
if (metric.includes('total') || metric.includes('count')) {
|
|
43
|
+
getCounter(metric).add(value, labels);
|
|
44
|
+
}
|
|
45
|
+
else if (metric.includes('duration') || metric.includes('ms') || metric.includes('score')) {
|
|
46
|
+
getHistogram(metric).record(value, labels);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Default to counter
|
|
50
|
+
getCounter(metric).add(value, labels);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Active spans tracked by run ID
|
|
54
|
+
const activeSpans = new Map();
|
|
55
|
+
return {
|
|
56
|
+
get enabled() {
|
|
57
|
+
return enabled;
|
|
58
|
+
},
|
|
59
|
+
register(definition) {
|
|
60
|
+
metricStore.register(definition);
|
|
61
|
+
},
|
|
62
|
+
record(point) {
|
|
63
|
+
const result = metricStore.record(point);
|
|
64
|
+
forwardToOTel({ metric: point.metric, value: point.value, labels: point.labels });
|
|
65
|
+
return result;
|
|
66
|
+
},
|
|
67
|
+
current(metric, labels) {
|
|
68
|
+
return metricStore.current(metric, labels);
|
|
69
|
+
},
|
|
70
|
+
query(query) {
|
|
71
|
+
return metricStore.query(query);
|
|
72
|
+
},
|
|
73
|
+
summarize(metric, labels) {
|
|
74
|
+
return metricStore.summarize(metric, labels);
|
|
75
|
+
},
|
|
76
|
+
snapshot(labels) {
|
|
77
|
+
return metricStore.snapshot(labels);
|
|
78
|
+
},
|
|
79
|
+
definitions() {
|
|
80
|
+
return metricStore.definitions();
|
|
81
|
+
},
|
|
82
|
+
startPipelineSpan(runId, pipelineType) {
|
|
83
|
+
if (!enabled) {
|
|
84
|
+
return { end: () => { }, setAttribute: () => { } };
|
|
85
|
+
}
|
|
86
|
+
const attributes = {
|
|
87
|
+
[ATTRIBUTE_KEYS.RUN_ID]: runId,
|
|
88
|
+
[ATTRIBUTE_KEYS.PIPELINE]: pipelineType,
|
|
89
|
+
};
|
|
90
|
+
// Use withSpan to create a span (fire-and-forget style)
|
|
91
|
+
let endFn;
|
|
92
|
+
// Since withSpan is async, we track spans manually
|
|
93
|
+
const handle = {
|
|
94
|
+
end(_status) {
|
|
95
|
+
activeSpans.delete(runId);
|
|
96
|
+
endFn?.();
|
|
97
|
+
},
|
|
98
|
+
setAttribute(key, value) {
|
|
99
|
+
attributes[key] = value;
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
activeSpans.set(runId, handle);
|
|
103
|
+
return handle;
|
|
104
|
+
},
|
|
105
|
+
startStageSpan(parentRunId, stageName) {
|
|
106
|
+
if (!enabled) {
|
|
107
|
+
return { end: () => { }, setAttribute: () => { } };
|
|
108
|
+
}
|
|
109
|
+
const attributes = {
|
|
110
|
+
[ATTRIBUTE_KEYS.STAGE]: stageName,
|
|
111
|
+
[ATTRIBUTE_KEYS.RUN_ID]: parentRunId,
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
end() { },
|
|
115
|
+
setAttribute(key, value) {
|
|
116
|
+
attributes[key] = value;
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Check if OTel export is available (endpoint configured).
|
|
124
|
+
*/
|
|
125
|
+
export function isOTelAvailable() {
|
|
126
|
+
return !!process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=otel-exporter.js.map
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrator plugin interface — extension points for enterprise hooks.
|
|
3
|
+
*
|
|
4
|
+
* Plugins can intercept pipeline lifecycle events (before/after run, errors)
|
|
5
|
+
* and are initialized with shared context (state store, cost tracker, etc.).
|
|
6
|
+
*/
|
|
7
|
+
import type { StateStore } from './state/index.js';
|
|
8
|
+
import type { CostTracker } from './cost-tracker.js';
|
|
9
|
+
import type { AutonomyTracker } from './autonomy-tracker.js';
|
|
10
|
+
import type { Logger } from './logger.js';
|
|
11
|
+
import type { PipelineResult } from './execute.js';
|
|
12
|
+
import type { NotificationRouter } from './notifications/notification-router.js';
|
|
13
|
+
export interface PluginContext {
|
|
14
|
+
store?: StateStore;
|
|
15
|
+
costTracker?: CostTracker;
|
|
16
|
+
autonomyTracker?: AutonomyTracker;
|
|
17
|
+
notificationRouter?: NotificationRouter;
|
|
18
|
+
log: Logger;
|
|
19
|
+
}
|
|
20
|
+
export interface BeforeRunEvent {
|
|
21
|
+
runId: string;
|
|
22
|
+
issueNumber: number;
|
|
23
|
+
startedAt: string;
|
|
24
|
+
}
|
|
25
|
+
export interface AfterRunEvent {
|
|
26
|
+
runId: string;
|
|
27
|
+
issueNumber: number;
|
|
28
|
+
result: PipelineResult;
|
|
29
|
+
durationMs: number;
|
|
30
|
+
}
|
|
31
|
+
export interface RunErrorEvent {
|
|
32
|
+
runId: string;
|
|
33
|
+
issueNumber: number;
|
|
34
|
+
error: Error;
|
|
35
|
+
durationMs: number;
|
|
36
|
+
}
|
|
37
|
+
export interface OrchestratorPlugin {
|
|
38
|
+
name: string;
|
|
39
|
+
initialize?(context: PluginContext): void | Promise<void>;
|
|
40
|
+
beforeRun?(event: BeforeRunEvent): void | Promise<void>;
|
|
41
|
+
afterRun?(event: AfterRunEvent): void | Promise<void>;
|
|
42
|
+
onError?(event: RunErrorEvent): void | Promise<void>;
|
|
43
|
+
shutdown?(): void | Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=plugin.d.ts.map
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrator plugin interface — extension points for enterprise hooks.
|
|
3
|
+
*
|
|
4
|
+
* Plugins can intercept pipeline lifecycle events (before/after run, errors)
|
|
5
|
+
* and are initialized with shared context (state store, cost tracker, etc.).
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy evaluator integration — wraps advanced policy evaluators
|
|
3
|
+
* (Rego, CEL, ABAC, expression, LLM) for pipeline gate evaluation.
|
|
4
|
+
*/
|
|
5
|
+
import { type ExpressionEvaluator, type LLMEvaluator, type ABACPolicy, type AuthorizationHook, type ComplexityInput, type ComplexityResult, type GateResult, type Gate, type EvaluationContext } from '@ai-sdlc/reference';
|
|
6
|
+
/**
|
|
7
|
+
* Create a Rego-based policy evaluator for gate rules.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createPipelineRegoEvaluator(): ExpressionEvaluator;
|
|
10
|
+
/**
|
|
11
|
+
* Create a CEL-based policy evaluator for gate rules.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createPipelineCELEvaluator(): ExpressionEvaluator;
|
|
14
|
+
/**
|
|
15
|
+
* Create an ABAC authorization hook from a set of policies.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createPipelineABACHook(policies: ABACPolicy[]): AuthorizationHook;
|
|
18
|
+
/**
|
|
19
|
+
* Create a simple expression evaluator for ExpressionRule gates.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createPipelineExpressionEvaluator(): ExpressionEvaluator;
|
|
22
|
+
/**
|
|
23
|
+
* Create a stub LLM evaluator for testing LLM gate rules.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createPipelineLLMEvaluator(): LLMEvaluator;
|
|
26
|
+
/**
|
|
27
|
+
* Evaluate a single gate with a given context.
|
|
28
|
+
*/
|
|
29
|
+
export declare function evaluatePipelineGate(gate: Gate, ctx: EvaluationContext): GateResult;
|
|
30
|
+
/**
|
|
31
|
+
* Score issue complexity using the reference scoring function.
|
|
32
|
+
*/
|
|
33
|
+
export declare function scorePipelineComplexity(input: ComplexityInput): number;
|
|
34
|
+
/**
|
|
35
|
+
* Full complexity evaluation with routing recommendation.
|
|
36
|
+
*/
|
|
37
|
+
export declare function evaluatePipelineComplexityRouting(input: ComplexityInput): ComplexityResult;
|
|
38
|
+
export { createRegoEvaluator, createCELEvaluator, createABACAuthorizationHook, createSimpleExpressionEvaluator, createStubLLMEvaluator, evaluateGate, scoreComplexity, evaluateComplexity, checkPermission, checkConstraints, createAuthorizationHook, createTokenAuthenticator, parseDuration, DEFAULT_COOLDOWN_MS, DEFAULT_COMPLEXITY_FACTORS, DEFAULT_THRESHOLDS, } from '@ai-sdlc/reference';
|
|
39
|
+
export type { ExpressionEvaluator, LLMEvaluator, ABACPolicy, ABACContext, ComplexityInput, ComplexityResult, GateResult, } from '@ai-sdlc/reference';
|
|
40
|
+
//# sourceMappingURL=policy-evaluators.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy evaluator integration — wraps advanced policy evaluators
|
|
3
|
+
* (Rego, CEL, ABAC, expression, LLM) for pipeline gate evaluation.
|
|
4
|
+
*/
|
|
5
|
+
import { createRegoEvaluator, createCELEvaluator, createABACAuthorizationHook, createSimpleExpressionEvaluator, createStubLLMEvaluator, evaluateGate, scoreComplexity, evaluateComplexity, } from '@ai-sdlc/reference';
|
|
6
|
+
/**
|
|
7
|
+
* Create a Rego-based policy evaluator for gate rules.
|
|
8
|
+
*/
|
|
9
|
+
export function createPipelineRegoEvaluator() {
|
|
10
|
+
return createRegoEvaluator();
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Create a CEL-based policy evaluator for gate rules.
|
|
14
|
+
*/
|
|
15
|
+
export function createPipelineCELEvaluator() {
|
|
16
|
+
return createCELEvaluator();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Create an ABAC authorization hook from a set of policies.
|
|
20
|
+
*/
|
|
21
|
+
export function createPipelineABACHook(policies) {
|
|
22
|
+
const evaluator = createSimpleExpressionEvaluator();
|
|
23
|
+
return createABACAuthorizationHook(evaluator, policies);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create a simple expression evaluator for ExpressionRule gates.
|
|
27
|
+
*/
|
|
28
|
+
export function createPipelineExpressionEvaluator() {
|
|
29
|
+
return createSimpleExpressionEvaluator();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Create a stub LLM evaluator for testing LLM gate rules.
|
|
33
|
+
*/
|
|
34
|
+
export function createPipelineLLMEvaluator() {
|
|
35
|
+
return createStubLLMEvaluator([]);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Evaluate a single gate with a given context.
|
|
39
|
+
*/
|
|
40
|
+
export function evaluatePipelineGate(gate, ctx) {
|
|
41
|
+
return evaluateGate(gate, ctx);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Score issue complexity using the reference scoring function.
|
|
45
|
+
*/
|
|
46
|
+
export function scorePipelineComplexity(input) {
|
|
47
|
+
return scoreComplexity(input);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Full complexity evaluation with routing recommendation.
|
|
51
|
+
*/
|
|
52
|
+
export function evaluatePipelineComplexityRouting(input) {
|
|
53
|
+
return evaluateComplexity(input);
|
|
54
|
+
}
|
|
55
|
+
// Direct re-exports (passthrough)
|
|
56
|
+
export { createRegoEvaluator, createCELEvaluator, createABACAuthorizationHook, createSimpleExpressionEvaluator, createStubLLMEvaluator, evaluateGate, scoreComplexity, evaluateComplexity, checkPermission, checkConstraints, createAuthorizationHook, createTokenAuthenticator, parseDuration, DEFAULT_COOLDOWN_MS, DEFAULT_COMPLEXITY_FACTORS, DEFAULT_THRESHOLDS, } from '@ai-sdlc/reference';
|
|
57
|
+
//# sourceMappingURL=policy-evaluators.js.map
|