@build-astron-co/nimbus 0.2.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.
Files changed (313) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +628 -0
  3. package/bin/nimbus +38 -0
  4. package/package.json +80 -0
  5. package/src/__tests__/app.test.ts +76 -0
  6. package/src/__tests__/audit.test.ts +877 -0
  7. package/src/__tests__/circuit-breaker.test.ts +116 -0
  8. package/src/__tests__/cli-run.test.ts +115 -0
  9. package/src/__tests__/context-manager.test.ts +502 -0
  10. package/src/__tests__/context.test.ts +242 -0
  11. package/src/__tests__/enterprise.test.ts +401 -0
  12. package/src/__tests__/generator.test.ts +433 -0
  13. package/src/__tests__/hooks.test.ts +582 -0
  14. package/src/__tests__/init.test.ts +436 -0
  15. package/src/__tests__/intent-parser.test.ts +229 -0
  16. package/src/__tests__/llm-router.test.ts +209 -0
  17. package/src/__tests__/lsp.test.ts +293 -0
  18. package/src/__tests__/modes.test.ts +336 -0
  19. package/src/__tests__/permissions.test.ts +338 -0
  20. package/src/__tests__/serve.test.ts +275 -0
  21. package/src/__tests__/sessions.test.ts +227 -0
  22. package/src/__tests__/sharing.test.ts +288 -0
  23. package/src/__tests__/snapshots.test.ts +581 -0
  24. package/src/__tests__/state-db.test.ts +334 -0
  25. package/src/__tests__/stream-with-tools.test.ts +732 -0
  26. package/src/__tests__/subagents.test.ts +176 -0
  27. package/src/__tests__/system-prompt.test.ts +169 -0
  28. package/src/__tests__/tool-converter.test.ts +256 -0
  29. package/src/__tests__/tool-schemas.test.ts +397 -0
  30. package/src/__tests__/tools.test.ts +143 -0
  31. package/src/__tests__/version.test.ts +49 -0
  32. package/src/agent/compaction-agent.ts +227 -0
  33. package/src/agent/context-manager.ts +435 -0
  34. package/src/agent/context.ts +427 -0
  35. package/src/agent/deploy-preview.ts +426 -0
  36. package/src/agent/index.ts +68 -0
  37. package/src/agent/loop.ts +717 -0
  38. package/src/agent/modes.ts +429 -0
  39. package/src/agent/permissions.ts +466 -0
  40. package/src/agent/subagents/base.ts +116 -0
  41. package/src/agent/subagents/cost.ts +51 -0
  42. package/src/agent/subagents/explore.ts +42 -0
  43. package/src/agent/subagents/general.ts +54 -0
  44. package/src/agent/subagents/index.ts +102 -0
  45. package/src/agent/subagents/infra.ts +59 -0
  46. package/src/agent/subagents/security.ts +69 -0
  47. package/src/agent/system-prompt.ts +436 -0
  48. package/src/app.ts +122 -0
  49. package/src/audit/activity-log.ts +290 -0
  50. package/src/audit/compliance-checker.ts +540 -0
  51. package/src/audit/cost-tracker.ts +318 -0
  52. package/src/audit/index.ts +23 -0
  53. package/src/audit/security-scanner.ts +596 -0
  54. package/src/auth/guard.ts +75 -0
  55. package/src/auth/index.ts +56 -0
  56. package/src/auth/oauth.ts +455 -0
  57. package/src/auth/providers.ts +470 -0
  58. package/src/auth/sso.ts +113 -0
  59. package/src/auth/store.ts +505 -0
  60. package/src/auth/types.ts +187 -0
  61. package/src/build.ts +141 -0
  62. package/src/cli/index.ts +16 -0
  63. package/src/cli/init.ts +854 -0
  64. package/src/cli/openapi-spec.ts +356 -0
  65. package/src/cli/run.ts +237 -0
  66. package/src/cli/serve-auth.ts +80 -0
  67. package/src/cli/serve.ts +462 -0
  68. package/src/cli/web.ts +67 -0
  69. package/src/cli.ts +1417 -0
  70. package/src/clients/core-engine-client.ts +227 -0
  71. package/src/clients/enterprise-client.ts +334 -0
  72. package/src/clients/generator-client.ts +351 -0
  73. package/src/clients/git-client.ts +627 -0
  74. package/src/clients/github-client.ts +410 -0
  75. package/src/clients/helm-client.ts +504 -0
  76. package/src/clients/index.ts +80 -0
  77. package/src/clients/k8s-client.ts +497 -0
  78. package/src/clients/llm-client.ts +161 -0
  79. package/src/clients/rest-client.ts +130 -0
  80. package/src/clients/service-discovery.ts +33 -0
  81. package/src/clients/terraform-client.ts +482 -0
  82. package/src/clients/tools-client.ts +1843 -0
  83. package/src/clients/ws-client.ts +115 -0
  84. package/src/commands/analyze/index.ts +352 -0
  85. package/src/commands/apply/helm.ts +473 -0
  86. package/src/commands/apply/index.ts +213 -0
  87. package/src/commands/apply/k8s.ts +454 -0
  88. package/src/commands/apply/terraform.ts +582 -0
  89. package/src/commands/ask.ts +167 -0
  90. package/src/commands/audit/index.ts +238 -0
  91. package/src/commands/auth-cloud.ts +294 -0
  92. package/src/commands/auth-list.ts +134 -0
  93. package/src/commands/auth-profile.ts +121 -0
  94. package/src/commands/auth-status.ts +141 -0
  95. package/src/commands/aws/ec2.ts +501 -0
  96. package/src/commands/aws/iam.ts +397 -0
  97. package/src/commands/aws/index.ts +133 -0
  98. package/src/commands/aws/lambda.ts +396 -0
  99. package/src/commands/aws/rds.ts +439 -0
  100. package/src/commands/aws/s3.ts +439 -0
  101. package/src/commands/aws/vpc.ts +393 -0
  102. package/src/commands/aws-discover.ts +649 -0
  103. package/src/commands/aws-terraform.ts +805 -0
  104. package/src/commands/azure/aks.ts +376 -0
  105. package/src/commands/azure/functions.ts +253 -0
  106. package/src/commands/azure/index.ts +116 -0
  107. package/src/commands/azure/storage.ts +478 -0
  108. package/src/commands/azure/vm.ts +355 -0
  109. package/src/commands/billing/index.ts +256 -0
  110. package/src/commands/chat.ts +314 -0
  111. package/src/commands/config.ts +346 -0
  112. package/src/commands/cost/cloud-cost-estimator.ts +266 -0
  113. package/src/commands/cost/estimator.ts +79 -0
  114. package/src/commands/cost/index.ts +594 -0
  115. package/src/commands/cost/parsers/terraform.ts +273 -0
  116. package/src/commands/cost/parsers/types.ts +25 -0
  117. package/src/commands/cost/pricing/aws.ts +544 -0
  118. package/src/commands/cost/pricing/azure.ts +499 -0
  119. package/src/commands/cost/pricing/gcp.ts +396 -0
  120. package/src/commands/cost/pricing/index.ts +40 -0
  121. package/src/commands/demo.ts +250 -0
  122. package/src/commands/doctor.ts +794 -0
  123. package/src/commands/drift/index.ts +439 -0
  124. package/src/commands/explain.ts +277 -0
  125. package/src/commands/feedback.ts +389 -0
  126. package/src/commands/fix.ts +324 -0
  127. package/src/commands/fs/index.ts +402 -0
  128. package/src/commands/gcp/compute.ts +325 -0
  129. package/src/commands/gcp/functions.ts +271 -0
  130. package/src/commands/gcp/gke.ts +438 -0
  131. package/src/commands/gcp/iam.ts +344 -0
  132. package/src/commands/gcp/index.ts +129 -0
  133. package/src/commands/gcp/storage.ts +284 -0
  134. package/src/commands/generate-helm.ts +1249 -0
  135. package/src/commands/generate-k8s.ts +1560 -0
  136. package/src/commands/generate-terraform.ts +1460 -0
  137. package/src/commands/gh/index.ts +863 -0
  138. package/src/commands/git/index.ts +1343 -0
  139. package/src/commands/helm/index.ts +1126 -0
  140. package/src/commands/help.ts +539 -0
  141. package/src/commands/history.ts +142 -0
  142. package/src/commands/import.ts +868 -0
  143. package/src/commands/index.ts +367 -0
  144. package/src/commands/init.ts +1046 -0
  145. package/src/commands/k8s/index.ts +1137 -0
  146. package/src/commands/login.ts +631 -0
  147. package/src/commands/logout.ts +83 -0
  148. package/src/commands/onboarding.ts +228 -0
  149. package/src/commands/plan/display.ts +279 -0
  150. package/src/commands/plan/index.ts +599 -0
  151. package/src/commands/preview.ts +452 -0
  152. package/src/commands/questionnaire.ts +1270 -0
  153. package/src/commands/resume.ts +55 -0
  154. package/src/commands/team/index.ts +346 -0
  155. package/src/commands/template.ts +232 -0
  156. package/src/commands/tf/index.ts +1034 -0
  157. package/src/commands/upgrade.ts +550 -0
  158. package/src/commands/usage/index.ts +134 -0
  159. package/src/commands/version.ts +170 -0
  160. package/src/compat/index.ts +2 -0
  161. package/src/compat/runtime.ts +12 -0
  162. package/src/compat/sqlite.ts +107 -0
  163. package/src/config/index.ts +17 -0
  164. package/src/config/manager.ts +530 -0
  165. package/src/config/safety-policy.ts +358 -0
  166. package/src/config/schema.ts +125 -0
  167. package/src/config/types.ts +527 -0
  168. package/src/context/context-db.ts +199 -0
  169. package/src/demo/index.ts +349 -0
  170. package/src/demo/scenarios/full-journey.ts +229 -0
  171. package/src/demo/scenarios/getting-started.ts +127 -0
  172. package/src/demo/scenarios/helm-release.ts +341 -0
  173. package/src/demo/scenarios/k8s-deployment.ts +194 -0
  174. package/src/demo/scenarios/terraform-vpc.ts +170 -0
  175. package/src/demo/types.ts +92 -0
  176. package/src/engine/cost-estimator.ts +438 -0
  177. package/src/engine/diagram-generator.ts +256 -0
  178. package/src/engine/drift-detector.ts +902 -0
  179. package/src/engine/executor.ts +1035 -0
  180. package/src/engine/index.ts +76 -0
  181. package/src/engine/orchestrator.ts +636 -0
  182. package/src/engine/planner.ts +720 -0
  183. package/src/engine/safety.ts +743 -0
  184. package/src/engine/verifier.ts +770 -0
  185. package/src/enterprise/audit.ts +348 -0
  186. package/src/enterprise/auth.ts +270 -0
  187. package/src/enterprise/billing.ts +822 -0
  188. package/src/enterprise/index.ts +17 -0
  189. package/src/enterprise/teams.ts +443 -0
  190. package/src/generator/best-practices.ts +1608 -0
  191. package/src/generator/helm.ts +630 -0
  192. package/src/generator/index.ts +37 -0
  193. package/src/generator/intent-parser.ts +514 -0
  194. package/src/generator/kubernetes.ts +976 -0
  195. package/src/generator/terraform.ts +1867 -0
  196. package/src/history/index.ts +8 -0
  197. package/src/history/manager.ts +322 -0
  198. package/src/history/types.ts +34 -0
  199. package/src/hooks/config.ts +432 -0
  200. package/src/hooks/engine.ts +391 -0
  201. package/src/hooks/index.ts +4 -0
  202. package/src/llm/auth-bridge.ts +198 -0
  203. package/src/llm/circuit-breaker.ts +140 -0
  204. package/src/llm/config-loader.ts +201 -0
  205. package/src/llm/cost-calculator.ts +171 -0
  206. package/src/llm/index.ts +8 -0
  207. package/src/llm/model-aliases.ts +115 -0
  208. package/src/llm/provider-registry.ts +63 -0
  209. package/src/llm/providers/anthropic.ts +433 -0
  210. package/src/llm/providers/bedrock.ts +477 -0
  211. package/src/llm/providers/google.ts +405 -0
  212. package/src/llm/providers/ollama.ts +767 -0
  213. package/src/llm/providers/openai-compatible.ts +340 -0
  214. package/src/llm/providers/openai.ts +328 -0
  215. package/src/llm/providers/openrouter.ts +338 -0
  216. package/src/llm/router.ts +1035 -0
  217. package/src/llm/types.ts +232 -0
  218. package/src/lsp/client.ts +298 -0
  219. package/src/lsp/languages.ts +116 -0
  220. package/src/lsp/manager.ts +278 -0
  221. package/src/mcp/client.ts +402 -0
  222. package/src/mcp/index.ts +5 -0
  223. package/src/mcp/manager.ts +133 -0
  224. package/src/nimbus.ts +214 -0
  225. package/src/plugins/index.ts +27 -0
  226. package/src/plugins/loader.ts +334 -0
  227. package/src/plugins/manager.ts +376 -0
  228. package/src/plugins/types.ts +284 -0
  229. package/src/scanners/cicd-scanner.ts +258 -0
  230. package/src/scanners/cloud-scanner.ts +466 -0
  231. package/src/scanners/framework-scanner.ts +469 -0
  232. package/src/scanners/iac-scanner.ts +388 -0
  233. package/src/scanners/index.ts +539 -0
  234. package/src/scanners/language-scanner.ts +276 -0
  235. package/src/scanners/package-manager-scanner.ts +277 -0
  236. package/src/scanners/types.ts +172 -0
  237. package/src/sessions/manager.ts +365 -0
  238. package/src/sessions/types.ts +44 -0
  239. package/src/sharing/sync.ts +296 -0
  240. package/src/sharing/viewer.ts +97 -0
  241. package/src/snapshots/index.ts +2 -0
  242. package/src/snapshots/manager.ts +530 -0
  243. package/src/state/artifacts.ts +147 -0
  244. package/src/state/audit.ts +137 -0
  245. package/src/state/billing.ts +240 -0
  246. package/src/state/checkpoints.ts +117 -0
  247. package/src/state/config.ts +67 -0
  248. package/src/state/conversations.ts +14 -0
  249. package/src/state/credentials.ts +154 -0
  250. package/src/state/db.ts +58 -0
  251. package/src/state/index.ts +26 -0
  252. package/src/state/messages.ts +115 -0
  253. package/src/state/projects.ts +123 -0
  254. package/src/state/schema.ts +236 -0
  255. package/src/state/sessions.ts +147 -0
  256. package/src/state/teams.ts +200 -0
  257. package/src/telemetry.ts +108 -0
  258. package/src/tools/aws-ops.ts +952 -0
  259. package/src/tools/azure-ops.ts +579 -0
  260. package/src/tools/file-ops.ts +593 -0
  261. package/src/tools/gcp-ops.ts +625 -0
  262. package/src/tools/git-ops.ts +773 -0
  263. package/src/tools/github-ops.ts +799 -0
  264. package/src/tools/helm-ops.ts +943 -0
  265. package/src/tools/index.ts +17 -0
  266. package/src/tools/k8s-ops.ts +819 -0
  267. package/src/tools/schemas/converter.ts +184 -0
  268. package/src/tools/schemas/devops.ts +612 -0
  269. package/src/tools/schemas/index.ts +73 -0
  270. package/src/tools/schemas/standard.ts +1144 -0
  271. package/src/tools/schemas/types.ts +705 -0
  272. package/src/tools/terraform-ops.ts +862 -0
  273. package/src/types/ambient.d.ts +193 -0
  274. package/src/types/config.ts +83 -0
  275. package/src/types/drift.ts +116 -0
  276. package/src/types/enterprise.ts +335 -0
  277. package/src/types/index.ts +20 -0
  278. package/src/types/plan.ts +44 -0
  279. package/src/types/request.ts +65 -0
  280. package/src/types/response.ts +54 -0
  281. package/src/types/service.ts +51 -0
  282. package/src/ui/App.tsx +997 -0
  283. package/src/ui/DeployPreview.tsx +169 -0
  284. package/src/ui/Header.tsx +68 -0
  285. package/src/ui/InputBox.tsx +350 -0
  286. package/src/ui/MessageList.tsx +585 -0
  287. package/src/ui/PermissionPrompt.tsx +151 -0
  288. package/src/ui/StatusBar.tsx +158 -0
  289. package/src/ui/ToolCallDisplay.tsx +409 -0
  290. package/src/ui/chat-ui.ts +853 -0
  291. package/src/ui/index.ts +33 -0
  292. package/src/ui/ink/index.ts +711 -0
  293. package/src/ui/streaming.ts +176 -0
  294. package/src/ui/types.ts +57 -0
  295. package/src/utils/analytics.ts +72 -0
  296. package/src/utils/cost-warning.ts +27 -0
  297. package/src/utils/env.ts +46 -0
  298. package/src/utils/errors.ts +69 -0
  299. package/src/utils/event-bus.ts +38 -0
  300. package/src/utils/index.ts +24 -0
  301. package/src/utils/logger.ts +171 -0
  302. package/src/utils/rate-limiter.ts +121 -0
  303. package/src/utils/service-auth.ts +49 -0
  304. package/src/utils/validation.ts +53 -0
  305. package/src/version.ts +4 -0
  306. package/src/watcher/index.ts +163 -0
  307. package/src/wizard/approval.ts +383 -0
  308. package/src/wizard/index.ts +25 -0
  309. package/src/wizard/prompts.ts +338 -0
  310. package/src/wizard/types.ts +171 -0
  311. package/src/wizard/ui.ts +556 -0
  312. package/src/wizard/wizard.ts +304 -0
  313. package/tsconfig.json +24 -0
@@ -0,0 +1,743 @@
1
+ import { logger } from '../utils';
2
+ import type { AgentTask, AgentPlan } from './planner';
3
+
4
+ // ==========================================
5
+ // Safety Check Types (inline — no HTTP)
6
+ // ==========================================
7
+
8
+ export interface SafetyCheckResult {
9
+ passed: boolean;
10
+ severity: 'low' | 'medium' | 'high' | 'critical';
11
+ message: string;
12
+ details?: Record<string, unknown>;
13
+ can_proceed: boolean;
14
+ requires_approval: boolean;
15
+ }
16
+
17
+ export interface SafetyCheck {
18
+ id: string;
19
+ type: 'pre_execution' | 'during_execution' | 'post_execution';
20
+ category: 'security' | 'cost' | 'availability' | 'compliance';
21
+ name: string;
22
+ description: string;
23
+ severity: 'low' | 'medium' | 'high' | 'critical';
24
+ check: (context: Record<string, unknown>) => Promise<SafetyCheckResult>;
25
+ }
26
+
27
+ // ==========================================
28
+ // SafetyManager
29
+ // ==========================================
30
+
31
+ export class SafetyManager {
32
+ private checks: Map<string, SafetyCheck>;
33
+
34
+ constructor() {
35
+ this.checks = new Map();
36
+ this.initializeDefaultChecks();
37
+ }
38
+
39
+ /**
40
+ * Run pre-execution safety checks
41
+ */
42
+ async runPreExecutionChecks(
43
+ task: AgentTask,
44
+ plan: AgentPlan
45
+ ): Promise<{ passed: boolean; results: SafetyCheckResult[]; blockers: SafetyCheckResult[] }> {
46
+ logger.info(`Running pre-execution safety checks for task: ${task.id}`);
47
+
48
+ const preExecutionChecks = Array.from(this.checks.values()).filter(
49
+ check => check.type === 'pre_execution'
50
+ );
51
+
52
+ const results: SafetyCheckResult[] = [];
53
+ const context = {
54
+ task,
55
+ plan,
56
+ environment: task.context.environment,
57
+ provider: task.context.provider,
58
+ components: task.context.components,
59
+ };
60
+
61
+ for (const check of preExecutionChecks) {
62
+ try {
63
+ const result = await check.check(context);
64
+ results.push(result);
65
+
66
+ logger.info(
67
+ `Safety check ${check.id}: ${result.passed ? 'PASSED' : 'FAILED'} (${check.name})`
68
+ );
69
+ } catch (error) {
70
+ logger.error(`Error running safety check ${check.id}`, error);
71
+ results.push({
72
+ passed: false,
73
+ severity: 'high',
74
+ message: `Check failed with error: ${(error as Error).message}`,
75
+ can_proceed: false,
76
+ requires_approval: true,
77
+ });
78
+ }
79
+ }
80
+
81
+ // Identify blockers (failed checks that prevent execution)
82
+ const blockers = results.filter(r => !r.passed && !r.can_proceed);
83
+
84
+ const passed = blockers.length === 0;
85
+
86
+ logger.info(
87
+ `Pre-execution checks: ${results.length} total, ${results.filter(r => r.passed).length} passed, ${blockers.length} blockers`
88
+ );
89
+
90
+ return { passed, results, blockers };
91
+ }
92
+
93
+ /**
94
+ * Run during-execution safety checks
95
+ */
96
+ async runDuringExecutionChecks(
97
+ context: Record<string, unknown>
98
+ ): Promise<{ passed: boolean; results: SafetyCheckResult[] }> {
99
+ logger.info('Running during-execution safety checks');
100
+
101
+ const duringExecutionChecks = Array.from(this.checks.values()).filter(
102
+ check => check.type === 'during_execution'
103
+ );
104
+
105
+ const results: SafetyCheckResult[] = [];
106
+
107
+ for (const check of duringExecutionChecks) {
108
+ try {
109
+ const result = await check.check(context);
110
+ results.push(result);
111
+ } catch (error) {
112
+ logger.error(`Error running safety check ${check.id}`, error);
113
+ results.push({
114
+ passed: false,
115
+ severity: 'high',
116
+ message: `Check failed with error: ${(error as Error).message}`,
117
+ can_proceed: false,
118
+ requires_approval: true,
119
+ });
120
+ }
121
+ }
122
+
123
+ const passed = results.every(r => r.passed || r.can_proceed);
124
+
125
+ return { passed, results };
126
+ }
127
+
128
+ /**
129
+ * Run post-execution safety checks
130
+ */
131
+ async runPostExecutionChecks(
132
+ context: Record<string, unknown>
133
+ ): Promise<{ passed: boolean; results: SafetyCheckResult[] }> {
134
+ logger.info('Running post-execution safety checks');
135
+
136
+ const postExecutionChecks = Array.from(this.checks.values()).filter(
137
+ check => check.type === 'post_execution'
138
+ );
139
+
140
+ const results: SafetyCheckResult[] = [];
141
+
142
+ for (const check of postExecutionChecks) {
143
+ try {
144
+ const result = await check.check(context);
145
+ results.push(result);
146
+ } catch (error) {
147
+ logger.error(`Error running safety check ${check.id}`, error);
148
+ results.push({
149
+ passed: false,
150
+ severity: 'high',
151
+ message: `Check failed with error: ${(error as Error).message}`,
152
+ can_proceed: true, // Post-execution failures don't block
153
+ requires_approval: false,
154
+ });
155
+ }
156
+ }
157
+
158
+ const passed = results.every(r => r.passed);
159
+
160
+ return { passed, results };
161
+ }
162
+
163
+ /**
164
+ * Register custom safety check
165
+ */
166
+ registerCheck(check: SafetyCheck): void {
167
+ this.checks.set(check.id, check);
168
+ logger.info(`Registered safety check: ${check.id}`);
169
+ }
170
+
171
+ /**
172
+ * Remove safety check
173
+ */
174
+ removeCheck(checkId: string): void {
175
+ this.checks.delete(checkId);
176
+ logger.info(`Removed safety check: ${checkId}`);
177
+ }
178
+
179
+ /**
180
+ * Get all checks
181
+ */
182
+ getAllChecks(): SafetyCheck[] {
183
+ return Array.from(this.checks.values());
184
+ }
185
+
186
+ /**
187
+ * Initialize default safety checks
188
+ */
189
+ private initializeDefaultChecks(): void {
190
+ // ===== Pre-Execution Checks =====
191
+
192
+ // Check: Production environment safeguards
193
+ this.registerCheck({
194
+ id: 'pre_prod_safeguard',
195
+ type: 'pre_execution',
196
+ category: 'security',
197
+ name: 'Production Environment Safeguards',
198
+ description: 'Verify additional safeguards for production deployments',
199
+ severity: 'critical',
200
+ check: async context => {
201
+ const task = context.task as AgentTask;
202
+ const plan = context.plan as AgentPlan;
203
+
204
+ if (task.context.environment === 'production') {
205
+ // Production requires approval
206
+ if (!plan.requires_approval || !plan.approved_by) {
207
+ return {
208
+ passed: false,
209
+ severity: 'critical',
210
+ message: 'Production deployment requires explicit approval',
211
+ can_proceed: false,
212
+ requires_approval: true,
213
+ };
214
+ }
215
+
216
+ // Check for high-risk operations
217
+ if (plan.risk_level === 'high' || plan.risk_level === 'critical') {
218
+ return {
219
+ passed: true,
220
+ severity: 'high',
221
+ message: 'High-risk production deployment approved',
222
+ details: {
223
+ risk_level: plan.risk_level,
224
+ approved_by: plan.approved_by,
225
+ },
226
+ can_proceed: true,
227
+ requires_approval: false,
228
+ };
229
+ }
230
+ }
231
+
232
+ return {
233
+ passed: true,
234
+ severity: 'low',
235
+ message: 'Environment safeguards satisfied',
236
+ can_proceed: true,
237
+ requires_approval: false,
238
+ };
239
+ },
240
+ });
241
+
242
+ // Check: Cost limits
243
+ this.registerCheck({
244
+ id: 'pre_cost_limit',
245
+ type: 'pre_execution',
246
+ category: 'cost',
247
+ name: 'Cost Limit Check',
248
+ description: 'Verify estimated cost is within acceptable limits',
249
+ severity: 'high',
250
+ check: async context => {
251
+ const plan = context.plan as AgentPlan;
252
+ const estimatedCost = plan.estimated_cost || 0;
253
+ const maxCost = 5000; // $5000 per month limit
254
+
255
+ if (estimatedCost > maxCost) {
256
+ return {
257
+ passed: false,
258
+ severity: 'high',
259
+ message: `Estimated cost $${estimatedCost} exceeds limit $${maxCost}`,
260
+ details: {
261
+ estimated: estimatedCost,
262
+ limit: maxCost,
263
+ },
264
+ can_proceed: false,
265
+ requires_approval: true,
266
+ };
267
+ }
268
+
269
+ return {
270
+ passed: true,
271
+ severity: 'low',
272
+ message: `Estimated cost $${estimatedCost} within limit`,
273
+ can_proceed: true,
274
+ requires_approval: false,
275
+ };
276
+ },
277
+ });
278
+
279
+ // Check: Security best practices
280
+ this.registerCheck({
281
+ id: 'pre_security_practices',
282
+ type: 'pre_execution',
283
+ category: 'security',
284
+ name: 'Security Best Practices',
285
+ description: 'Verify security best practices are applied',
286
+ severity: 'critical',
287
+ check: async context => {
288
+ const plan = context.plan as AgentPlan;
289
+
290
+ // Check if security validation steps are included
291
+ const hasSecurityValidation = plan.steps.some(
292
+ step => step.action === 'apply_best_practices'
293
+ );
294
+
295
+ if (!hasSecurityValidation) {
296
+ return {
297
+ passed: false,
298
+ severity: 'critical',
299
+ message: 'Plan missing security best practices validation',
300
+ can_proceed: false,
301
+ requires_approval: true,
302
+ };
303
+ }
304
+
305
+ return {
306
+ passed: true,
307
+ severity: 'low',
308
+ message: 'Security best practices validation included',
309
+ can_proceed: true,
310
+ requires_approval: false,
311
+ };
312
+ },
313
+ });
314
+
315
+ // Check: Backup strategy
316
+ this.registerCheck({
317
+ id: 'pre_backup_strategy',
318
+ type: 'pre_execution',
319
+ category: 'availability',
320
+ name: 'Backup Strategy',
321
+ description: 'Verify backup strategy is defined for stateful components',
322
+ severity: 'high',
323
+ check: async context => {
324
+ const task = context.task as AgentTask;
325
+ const hasStatefulComponents = task.context.components.some(c =>
326
+ ['rds', 's3', 'efs'].includes(c)
327
+ );
328
+
329
+ if (hasStatefulComponents) {
330
+ // Check if backup is configured
331
+ const hasBackup = task.context.requirements?.backup_enabled === true;
332
+
333
+ if (!hasBackup && task.context.environment === 'production') {
334
+ return {
335
+ passed: false,
336
+ severity: 'high',
337
+ message: 'Production stateful components require backup strategy',
338
+ can_proceed: false,
339
+ requires_approval: true,
340
+ };
341
+ }
342
+
343
+ if (!hasBackup) {
344
+ return {
345
+ passed: true,
346
+ severity: 'medium',
347
+ message: 'Backup recommended for stateful components',
348
+ can_proceed: true,
349
+ requires_approval: false,
350
+ };
351
+ }
352
+ }
353
+
354
+ return {
355
+ passed: true,
356
+ severity: 'low',
357
+ message: 'Backup strategy validated',
358
+ can_proceed: true,
359
+ requires_approval: false,
360
+ };
361
+ },
362
+ });
363
+
364
+ // Check: Destructive operations
365
+ this.registerCheck({
366
+ id: 'pre_destructive_ops',
367
+ type: 'pre_execution',
368
+ category: 'availability',
369
+ name: 'Destructive Operations',
370
+ description: 'Verify destructive operations are intentional',
371
+ severity: 'critical',
372
+ check: async context => {
373
+ const plan = context.plan as AgentPlan;
374
+
375
+ // Check for deployment steps that could be destructive
376
+ const hasDeployment = plan.steps.some(step => step.action === 'apply_deployment');
377
+
378
+ if (hasDeployment) {
379
+ // Ensure plan has rollback capability
380
+ const hasRollback = plan.steps.some(step => step.rollback_action);
381
+
382
+ if (!hasRollback) {
383
+ return {
384
+ passed: false,
385
+ severity: 'high',
386
+ message: 'Deployment steps should have rollback capability',
387
+ can_proceed: true, // Can proceed but warned
388
+ requires_approval: true,
389
+ };
390
+ }
391
+ }
392
+
393
+ return {
394
+ passed: true,
395
+ severity: 'low',
396
+ message: 'Destructive operations validated',
397
+ can_proceed: true,
398
+ requires_approval: false,
399
+ };
400
+ },
401
+ });
402
+
403
+ // ===== During-Execution Checks =====
404
+
405
+ // Check: Resource creation rate
406
+ this.registerCheck({
407
+ id: 'during_resource_rate',
408
+ type: 'during_execution',
409
+ category: 'cost',
410
+ name: 'Resource Creation Rate',
411
+ description: 'Monitor resource creation rate for anomalies',
412
+ severity: 'medium',
413
+ check: async context => {
414
+ // In production, this would monitor actual resource creation
415
+ const resourcesCreated = (context.resources_created as number) || 0;
416
+ const maxRate = 50; // Max 50 resources per execution
417
+
418
+ if (resourcesCreated > maxRate) {
419
+ return {
420
+ passed: false,
421
+ severity: 'high',
422
+ message: `Unusual resource creation rate: ${resourcesCreated} resources`,
423
+ details: { count: resourcesCreated, threshold: maxRate },
424
+ can_proceed: false,
425
+ requires_approval: true,
426
+ };
427
+ }
428
+
429
+ return {
430
+ passed: true,
431
+ severity: 'low',
432
+ message: 'Resource creation rate normal',
433
+ can_proceed: true,
434
+ requires_approval: false,
435
+ };
436
+ },
437
+ });
438
+
439
+ // Check: Execution timeout
440
+ this.registerCheck({
441
+ id: 'during_execution_timeout',
442
+ type: 'during_execution',
443
+ category: 'availability',
444
+ name: 'Execution Timeout',
445
+ description: 'Ensure execution does not exceed time limits',
446
+ severity: 'medium',
447
+ check: async context => {
448
+ const startTime = context.start_time as Date;
449
+ const maxDuration = 3600000; // 1 hour
450
+
451
+ if (startTime) {
452
+ const elapsed = Date.now() - startTime.getTime();
453
+
454
+ if (elapsed > maxDuration) {
455
+ return {
456
+ passed: false,
457
+ severity: 'high',
458
+ message: 'Execution exceeding time limit',
459
+ details: { elapsed, limit: maxDuration },
460
+ can_proceed: false,
461
+ requires_approval: false,
462
+ };
463
+ }
464
+ }
465
+
466
+ return {
467
+ passed: true,
468
+ severity: 'low',
469
+ message: 'Execution within time limits',
470
+ can_proceed: true,
471
+ requires_approval: false,
472
+ };
473
+ },
474
+ });
475
+
476
+ // ===== Post-Execution Checks =====
477
+
478
+ // Check: Deployment verification
479
+ this.registerCheck({
480
+ id: 'post_deployment_verify',
481
+ type: 'post_execution',
482
+ category: 'availability',
483
+ name: 'Deployment Verification',
484
+ description: 'Verify deployed resources are healthy',
485
+ severity: 'high',
486
+ check: async context => {
487
+ const deploymentSuccess = context.deployment_success as boolean;
488
+
489
+ if (deploymentSuccess === false) {
490
+ return {
491
+ passed: false,
492
+ severity: 'critical',
493
+ message: 'Deployment verification failed',
494
+ can_proceed: true, // Already deployed
495
+ requires_approval: false,
496
+ };
497
+ }
498
+
499
+ return {
500
+ passed: true,
501
+ severity: 'low',
502
+ message: 'Deployment verified successfully',
503
+ can_proceed: true,
504
+ requires_approval: false,
505
+ };
506
+ },
507
+ });
508
+
509
+ // Check: Cost anomaly detection
510
+ this.registerCheck({
511
+ id: 'post_cost_anomaly',
512
+ type: 'post_execution',
513
+ category: 'cost',
514
+ name: 'Cost Anomaly Detection',
515
+ description: 'Check for unexpected cost increases',
516
+ severity: 'medium',
517
+ check: async context => {
518
+ const estimatedCost = (context.estimated_cost as number) || 0;
519
+ const actualCost = (context.actual_cost as number) || estimatedCost;
520
+
521
+ // Allow 20% variance
522
+ const variance = Math.abs(actualCost - estimatedCost) / estimatedCost;
523
+
524
+ if (variance > 0.2) {
525
+ return {
526
+ passed: false,
527
+ severity: 'high',
528
+ message: `Actual cost differs significantly from estimate`,
529
+ details: {
530
+ estimated: estimatedCost,
531
+ actual: actualCost,
532
+ variance: `${(variance * 100).toFixed(1)}%`,
533
+ },
534
+ can_proceed: true,
535
+ requires_approval: false,
536
+ };
537
+ }
538
+
539
+ return {
540
+ passed: true,
541
+ severity: 'low',
542
+ message: 'Cost within expected range',
543
+ can_proceed: true,
544
+ requires_approval: false,
545
+ };
546
+ },
547
+ });
548
+
549
+ // Check: Security posture
550
+ this.registerCheck({
551
+ id: 'post_security_posture',
552
+ type: 'post_execution',
553
+ category: 'security',
554
+ name: 'Security Posture Assessment',
555
+ description: 'Assess final security configuration',
556
+ severity: 'critical',
557
+ check: async context => {
558
+ const securityScore = (context.security_score as number) || 0;
559
+ const minScore = 80;
560
+
561
+ if (securityScore < minScore) {
562
+ return {
563
+ passed: false,
564
+ severity: 'critical',
565
+ message: `Security score ${securityScore} below threshold ${minScore}`,
566
+ details: { score: securityScore, threshold: minScore },
567
+ can_proceed: true,
568
+ requires_approval: false,
569
+ };
570
+ }
571
+
572
+ return {
573
+ passed: true,
574
+ severity: 'low',
575
+ message: `Security score ${securityScore} meets requirements`,
576
+ can_proceed: true,
577
+ requires_approval: false,
578
+ };
579
+ },
580
+ });
581
+
582
+ // Check: No production delete without backup
583
+ this.registerCheck({
584
+ id: 'no_production_delete_without_backup',
585
+ type: 'pre_execution',
586
+ category: 'availability',
587
+ name: 'No Production Delete Without Backup',
588
+ description: 'Blocks destroy/delete operations in production when backup is not enabled',
589
+ severity: 'critical',
590
+ check: async context => {
591
+ const task = context.task as AgentTask;
592
+ const plan = context.plan as AgentPlan;
593
+
594
+ if (task.context.environment !== 'production') {
595
+ return {
596
+ passed: true,
597
+ severity: 'low',
598
+ message: 'Non-production environment, policy not applicable',
599
+ can_proceed: true,
600
+ requires_approval: false,
601
+ };
602
+ }
603
+
604
+ const hasDestructiveAction = plan.steps.some(
605
+ step =>
606
+ (step.action === 'apply_deployment' && step.parameters?.destroy) ||
607
+ (step.action === 'apply_deployment' &&
608
+ step.description?.toLowerCase().includes('delete')) ||
609
+ step.description?.toLowerCase().includes('destroy')
610
+ );
611
+
612
+ if (!hasDestructiveAction) {
613
+ return {
614
+ passed: true,
615
+ severity: 'low',
616
+ message: 'No destructive actions found in plan',
617
+ can_proceed: true,
618
+ requires_approval: false,
619
+ };
620
+ }
621
+
622
+ const backupEnabled = task.context.requirements?.backup_enabled === true;
623
+
624
+ if (!backupEnabled) {
625
+ return {
626
+ passed: false,
627
+ severity: 'critical',
628
+ message:
629
+ 'Production destroy/delete operations require backup_enabled in requirements. ' +
630
+ 'Create a backup first or use a staging environment.',
631
+ can_proceed: false,
632
+ requires_approval: true,
633
+ };
634
+ }
635
+
636
+ return {
637
+ passed: true,
638
+ severity: 'low',
639
+ message: 'Backup enabled for production destructive operation',
640
+ can_proceed: true,
641
+ requires_approval: false,
642
+ };
643
+ },
644
+ });
645
+
646
+ // Check: Require dry-run before apply
647
+ this.registerCheck({
648
+ id: 'require_dry_run_first',
649
+ type: 'pre_execution',
650
+ category: 'compliance',
651
+ name: 'Require Dry Run First',
652
+ description: 'Blocks apply_deployment if no plan_deployment step precedes it',
653
+ severity: 'high',
654
+ check: async context => {
655
+ const plan = context.plan as AgentPlan;
656
+
657
+ const hasApply = plan.steps.some(step => step.action === 'apply_deployment');
658
+
659
+ if (!hasApply) {
660
+ return {
661
+ passed: true,
662
+ severity: 'low',
663
+ message: 'No apply_deployment steps in plan',
664
+ can_proceed: true,
665
+ requires_approval: false,
666
+ };
667
+ }
668
+
669
+ const hasPlan = plan.steps.some(step => step.action === 'plan_deployment');
670
+
671
+ if (!hasPlan) {
672
+ return {
673
+ passed: false,
674
+ severity: 'high',
675
+ message:
676
+ 'Plan contains apply_deployment without a preceding plan_deployment step. ' +
677
+ 'Run with --dry-run first to preview changes.',
678
+ can_proceed: false,
679
+ requires_approval: true,
680
+ };
681
+ }
682
+
683
+ return {
684
+ passed: true,
685
+ severity: 'low',
686
+ message: 'Dry-run (plan_deployment) step precedes apply',
687
+ can_proceed: true,
688
+ requires_approval: false,
689
+ };
690
+ },
691
+ });
692
+
693
+ // Check: Token budget guardrail
694
+ this.registerCheck({
695
+ id: 'pre_token_budget',
696
+ type: 'pre_execution',
697
+ category: 'cost',
698
+ name: 'Token Budget Check',
699
+ description: 'Verify estimated token usage does not exceed budget',
700
+ severity: 'high',
701
+ check: async context => {
702
+ const plan = context.plan as AgentPlan & { estimated_tokens?: number };
703
+ const maxTokensPerTask = parseInt(process.env.MAX_TOKENS_PER_TASK || '0', 10);
704
+
705
+ if (!maxTokensPerTask || maxTokensPerTask <= 0) {
706
+ return {
707
+ passed: true,
708
+ severity: 'low',
709
+ message: 'No token budget configured',
710
+ can_proceed: true,
711
+ requires_approval: false,
712
+ };
713
+ }
714
+
715
+ const estimatedTokens = plan.estimated_tokens ?? plan.estimated_cost ?? 0;
716
+
717
+ if (estimatedTokens > maxTokensPerTask) {
718
+ return {
719
+ passed: false,
720
+ severity: 'high',
721
+ message: `Estimated token usage (${estimatedTokens}) exceeds budget (${maxTokensPerTask})`,
722
+ details: {
723
+ estimated: estimatedTokens,
724
+ budget: maxTokensPerTask,
725
+ },
726
+ can_proceed: false,
727
+ requires_approval: true,
728
+ };
729
+ }
730
+
731
+ return {
732
+ passed: true,
733
+ severity: 'low',
734
+ message: `Estimated tokens (${estimatedTokens}) within budget (${maxTokensPerTask})`,
735
+ can_proceed: true,
736
+ requires_approval: false,
737
+ };
738
+ },
739
+ });
740
+
741
+ logger.info(`Initialized ${this.checks.size} default safety checks`);
742
+ }
743
+ }