Repository: kodustech/kodus-ai
Pull request #22: Feat azure repos
Author: login: Wellington01
Created: 2025-04-22T23:35:52Z | Merged: 2025-04-23T00:55:53Z

This pull request introduces a series of enhancements and new functionalities to the `kodus-ai` repository, specifically targeting Azure Repos integration. Key changes include:

1. **Azure Repos Integration**: 
   - New functionality for pull request management, comment handling, and repository operations in `azureRepos.service.ts`.
   - Addition of enums for Azure Pull Request voting and comment types in `azureRepoExtras.type.ts` to improve type safety and maintainability.
   - New methods and minor improvements in `azure-repos-request-helper.ts`.

2. **Code Review and Automation**:
   - Updates to `runCodeReview.use-case.ts` to expand allowed actions and improve the `isMerged` check.
   - Enhancements in `process-files-review.stage.ts` with a null-safe check for `foundComments` array length.

3. **Platform Integration**:
   - Simplified imports and removal of unused types in `finish-onboarding.use-case.ts`.
   - Changes to webhook handling and command detection logic in `receiveWebhook.use-case.ts`.

4. **Pull Request Management**:
   - New valid actions for pull request validation and improved payload status checks in `save.use-case.ts`.
   - Additional state checks for pull request status and improved closed date extraction in `pullRequests.service.ts`.

5. **Service Enhancements**:
   - Updates to `bitbucket.service.ts` for interface implementation, method improvements, and error handling.
   - Improvements in `github.service.ts` for type safety, error handling, and code organization.
   - New method signatures in `gitlab.service.ts` for commit handling, deployment frequency calculation, and pull request management.

6. **Cron Job and Comment Analysis**:
   - Changes to the PR approval cron job in `CheckIfPRCanBeApproved.cron.ts` for better type safety and error handling.
   - Azure DevOps thread ID handling and improved comment filtering in `commentAnalysis.service.ts`.

Overall, this pull request significantly enhances the Azure Repos integration and improves various aspects of code management, error handling, and type safety across the repository.


Diff:
diff --git a/package.json b/package.json
index 039414e05..3d04040d6 100644
--- a/package.json
+++ b/package.json
@@ -40,17 +40,17 @@
     "@anthropic-ai/sdk": "^0.39.0",
     "@aws-sdk/client-s3": "^3.787.0",
     "@faker-js/faker": "^9.7.0",
-    "@gitbeaker/rest": "^42.2.0",
+    "@gitbeaker/rest": "^42.4.0",
     "@golevelup/nestjs-rabbitmq": "^5.7.0",
     "@google/generative-ai": "^0.24.0",
     "@langchain/anthropic": "^0.3.18",
     "@langchain/cohere": "^0.3.3",
     "@langchain/community": "^0.3.41",
-    "@langchain/core": "^0.3.45",
+    "@langchain/core": "^0.3.46",
     "@langchain/google-genai": "^0.2.4",
     "@langchain/google-vertexai": "^0.2.4",
     "@langchain/mongodb": "^0.1.0",
-    "@langchain/openai": "^0.5.5",
+    "@langchain/openai": "^0.5.6",
     "@nestjs/axios": "^4.0.0",
     "@nestjs/cache-manager": "^3.0.1",
     "@nestjs/common": "^11.0.20",
@@ -110,8 +110,8 @@
     "joi": "^17.13.3",
     "js-yaml": "^4.1.0",
     "json5": "^2.2.3",
-    "langchain": "^0.3.22",
-    "langsmith": "0.3.18",
+    "langchain": "^0.3.23",
+    "langsmith": "0.3.20",
     "mailersend": "^2.5.0",
     "micromatch": "^4.0.8",
     "ml-kmeans": "^6.0.0",
@@ -123,11 +123,11 @@
     "nestjs-pino": "^4.4.0",
     "nodemailer-express-handlebars": "^7.0.0",
     "octokit": "^4.1.3",
-    "openai": "^4.95.0",
+    "openai": "^4.95.1",
     "p-limit": "^6.2.0",
     "passport": "^0.7.0",
     "passport-jwt": "^4.0.1",
-    "pg": "^8.14.1",
+    "pg": "^8.15.1",
     "pg-promise": "^11.13.0",
     "pgvector": "^0.2.0",
     "picomatch": "^4.0.2",
@@ -139,7 +139,7 @@
     "rxjs": "^7.8.2",
     "simple-git": "^3.27.0",
     "source-map-support": "^0.5.21",
-    "tiktoken": "^1.0.20",
+    "tiktoken": "^1.0.21",
     "tree-sitter": "^0.22.4",
     "tree-sitter-c-sharp": "^0.23.1",
     "tree-sitter-go": "^0.23.4",
@@ -178,11 +178,11 @@
     "@types/passport-jwt": "^4.0.1",
     "@types/ramda": "^0.30.2",
     "@types/supertest": "^6.0.3",
-    "@typescript-eslint/eslint-plugin": "^8.30.1",
-    "@typescript-eslint/parser": "^8.30.1",
+    "@typescript-eslint/eslint-plugin": "^8.31.0",
+    "@typescript-eslint/parser": "^8.31.0",
     "concurrently": "^9.1.2",
     "esbuild": "^0.25.2",
-    "eslint": "^9.24.0",
+    "eslint": "^9.25.1",
     "eslint-config-prettier": "^10.1.2",
     "eslint-plugin-prettier": "^5.2.6",
     "husky": "^9.1.7",
diff --git a/src/core/application/use-cases/automation/runCodeReview.use-case.ts b/src/core/application/use-cases/automation/runCodeReview.use-case.ts
index d3c332511..2ef670cd5 100644
--- a/src/core/application/use-cases/automation/runCodeReview.use-case.ts
+++ b/src/core/application/use-cases/automation/runCodeReview.use-case.ts
@@ -49,7 +49,7 @@ export class RunCodeReviewAutomationUseCase {
         private readonly codeManagement: CodeManagementService,
 
         private logger: PinoLoggerService,
-    ) { }
+    ) {}
 
     async execute(params: {
         payload: any;
@@ -189,11 +189,25 @@ export class RunCodeReviewAutomationUseCase {
     }
 
     private shouldRunAutomation(payload: any, platformType: PlatformType) {
-        const allowedActions = ['opened', 'synchronize', 'open', 'update', 'git.pullrequest.updated', 'git.pullrequest.created'];
+        const allowedActions = [
+            'opened',
+            'synchronize',
+            'open',
+            'update',
+            'git.pullrequest.updated',
+            'git.pullrequest.created',
+        ];
         const currentAction =
-            payload?.action || payload?.object_attributes?.action || payload?.eventType;
+            payload?.action ||
+            payload?.object_attributes?.action ||
+            payload?.eventType;
+
+        const isMerged =
+            payload?.object_attributes?.state === 'merged' ||
+            payload?.resource?.pullRequest?.status === 'completed' ||
+            payload?.resource?.status === 'completed' ||
+            false;
 
-        const isMerged = payload?.object_attributes?.state === 'merged';
         const isCommand = payload?.origin === 'command';
 
         // bitbucket has already been handled in the webhook validation
@@ -202,6 +216,11 @@ export class RunCodeReviewAutomationUseCase {
             platformType !== PlatformType.BITBUCKET &&
             (!allowedActions.includes(currentAction) || isMerged)
         ) {
+            this.logger.log({
+                message: 'Automation skipped',
+                context: RunCodeReviewAutomationUseCase.name,
+                metadata: { currentAction, isMerged, platformType },
+            });
             return false;
         }
 
diff --git a/src/core/application/use-cases/platformIntegration/codeManagement/finish-onboarding.use-case.ts b/src/core/application/use-cases/platformIntegration/codeManagement/finish-onboarding.use-case.ts
index 95470f514..03782e930 100644
--- a/src/core/application/use-cases/platformIntegration/codeManagement/finish-onboarding.use-case.ts
+++ b/src/core/application/use-cases/platformIntegration/codeManagement/finish-onboarding.use-case.ts
@@ -6,17 +6,13 @@ import { GenerateCodeReviewParameterUseCase } from '../../parameters/generate-co
 import { GenerateKodyRulesUseCase } from '../../kodyRules/generate-kody-rules.use-case';
 import { REQUEST } from '@nestjs/core';
 import { FindRulesInOrganizationByRuleFilterKodyRulesUseCase } from '../../kodyRules/find-rules-in-organization-by-filter.use-case';
-import {
-    IKodyRule,
-    KodyRulesStatus,
-} from '@/core/domain/kodyRules/interfaces/kodyRules.interface';
+import { KodyRulesStatus } from '@/core/domain/kodyRules/interfaces/kodyRules.interface';
 import { ChangeStatusKodyRulesUseCase } from '../../kodyRules/change-status-kody-rules.use-case';
 import {
     IParametersService,
     PARAMETERS_SERVICE_TOKEN,
 } from '@/core/domain/parameters/contracts/parameters.service.contract';
 import { ParametersKey } from '@/shared/domain/enums/parameters-key.enum';
-import { KodyLearningStatus } from '@/core/domain/parameters/types/configValue.type';
 
 @Injectable()
 export class FinishOnboardingUseCase {
@@ -42,7 +38,7 @@ export class FinishOnboardingUseCase {
         let platformConfig;
 
         try {
-            if (!this.request.user.organization.uuid) {
+            if (!this.request?.user?.organization?.uuid) {
                 throw new Error('Organization ID not found');
             }
 
diff --git a/src/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case.ts b/src/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case.ts
index af8927d1c..af34c703c 100644
--- a/src/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case.ts
+++ b/src/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case.ts
@@ -36,7 +36,7 @@ export class ReceiveWebhookUseCase implements IUseCase {
         private readonly integrationConfigService: IIntegrationConfigService,
 
         private readonly bitbuckerService: BitbucketService,
-    ) { }
+    ) {}
 
     public async execute(params: {
         payload: any;
@@ -90,7 +90,7 @@ export class ReceiveWebhookUseCase implements IUseCase {
                 case 'Note Hook':
                     if (
                         params.payload?.object_attributes?.action ===
-                        'create' &&
+                            'create' &&
                         !params.payload?.object_attributes?.change_position &&
                         !params.payload?.object_attributes?.type
                     ) {
@@ -111,7 +111,9 @@ export class ReceiveWebhookUseCase implements IUseCase {
                     break;
                 case 'ms.vss-code.git-pullrequest-comment-event':
                     const comment = params.payload?.resource.comment.content;
-                    const pullRequestId = params.payload?.resource.pullRequest.status === 'active';
+                    const pullRequestId =
+                        params.payload?.resource?.pullRequest?.status ===
+                        'active';
 
                     if (comment && pullRequestId) {
                         this.isStartCommand(params);
@@ -158,12 +160,16 @@ export class ReceiveWebhookUseCase implements IUseCase {
             return;
         }
 
-        const commandPattern = /@kody\s+start-review/i;
+        const commandPattern = /^\s*@kody\s+start-review/i;
         const isStartCommand = commandPattern.test(comment.body);
 
+        // procura exatamente: <!-- kody-codereview -->
+        const reviewMarkerPattern = /<!--\s*kody-codereview\s*-->/i;
+        const hasReviewMarker = reviewMarkerPattern.test(comment.body);
+
         const pullRequest = mappedPlatform.mapPullRequest({ payload });
 
-        if (isStartCommand) {
+        if (isStartCommand && !hasReviewMarker) {
             this.logger.log({
                 message: `@kody start command detected in PR#${pullRequest?.number}`,
                 context: ReceiveWebhookUseCase.name,
@@ -289,7 +295,7 @@ export class ReceiveWebhookUseCase implements IUseCase {
                 repoId,
             );
 
-        if (!configs || !configs.length) {
+        if (!configs || !configs?.length) {
             return false;
         }
 
@@ -318,10 +324,10 @@ export class ReceiveWebhookUseCase implements IUseCase {
 
         if (storedPR) {
             const prCommit = pullRequestCommits[pullRequestCommits.length - 1];
-            const storedPRCommitHashes = storedPR.commits?.map(
+            const storedPRCommitHashes = storedPR?.commits?.map(
                 (commit) => commit.sha,
             );
-            if (storedPRCommitHashes.includes(prCommit.sha)) {
+            if (storedPRCommitHashes?.includes(prCommit?.sha)) {
                 return false;
             }
         }
@@ -337,12 +343,4 @@ export class ReceiveWebhookUseCase implements IUseCase {
                 return false;
         }
     }
-
-    /**
-     * Verifica se um comentário do Azure DevOps contém o comando para iniciar review
-     */
-    private isAzureDevOpsStartCommand(comment: string): boolean {
-        const commandPattern = /@kody\s+start-review/i;
-        return commandPattern.test(comment);
-    }
 }
diff --git a/src/core/application/use-cases/pullRequests/save.use-case.ts b/src/core/application/use-cases/pullRequests/save.use-case.ts
index d075f87c0..6415d1be0 100644
--- a/src/core/application/use-cases/pullRequests/save.use-case.ts
+++ b/src/core/application/use-cases/pullRequests/save.use-case.ts
@@ -27,7 +27,7 @@ export class SavePullRequestUseCase {
         private readonly codeManagement: CodeManagementService,
 
         private readonly logger: PinoLoggerService,
-    ) { }
+    ) {}
 
     public async execute(params: {
         payload: any;
@@ -179,6 +179,8 @@ export class SavePullRequestUseCase {
             'review_request_removed',
             'assigned',
             'unassigned',
+            'active',
+            'completed',
         ] as const;
         const validObjectActions = [
             'open',
@@ -191,9 +193,9 @@ export class SavePullRequestUseCase {
         return (
             validActions.includes(payload?.action) ||
             validObjectActions.includes(payload?.object_attributes?.action) ||
+            validActions.includes(payload?.resource?.status) ||
+            validActions.includes(payload?.resource?.pullRequest?.status) ||
             platformType === PlatformType.BITBUCKET
         );
     }
-
-
 }
diff --git a/src/core/domain/azureRepos/entities/azureRepoExtras.type.ts b/src/core/domain/azureRepos/entities/azureRepoExtras.type.ts
index 67b0a9da9..d530ce550 100644
--- a/src/core/domain/azureRepos/entities/azureRepoExtras.type.ts
+++ b/src/core/domain/azureRepos/entities/azureRepoExtras.type.ts
@@ -7,7 +7,21 @@ export interface AzureRepoLink {
 export type EventConfig = {
     type: string;
     resourceVersion: '1.0' | '2.0';
-  };
+};
+
+export enum AzurePullRequestVote {
+    Approved = 10,
+    ApprovedWithSuggestions = 5,
+    NoVote = 0,
+    WaitingForAuthor = -5,
+    Rejected = -10,
+}
+
+export enum AzureRepoCommentTypeString {
+    TEXT = 'text',
+    CODE = 'codeChange',
+    SYSTEM = 'system',
+}
 
 export enum AzureRepoCommentType {
     /**
diff --git a/src/core/infrastructure/adapters/services/azureRepos.service.ts b/src/core/infrastructure/adapters/services/azureRepos.service.ts
index 214115fb3..60bbceda4 100644
--- a/src/core/infrastructure/adapters/services/azureRepos.service.ts
+++ b/src/core/infrastructure/adapters/services/azureRepos.service.ts
@@ -18,6 +18,7 @@ import {
     PullRequests,
     PullRequestWithFiles,
     PullRequestReviewComment,
+    OneSentenceSummaryItem,
 } from '@/core/domain/platformIntegrations/types/codeManagement/pullRequests.type';
 import { Repositories } from '@/core/domain/platformIntegrations/types/codeManagement/repositories.type';
 import { v4 as uuidv4, v4 } from 'uuid';
@@ -40,7 +41,11 @@ import { IntegrationEntity } from '@/core/domain/integrations/entities/integrati
 import { AzureReposRequestHelper } from './azureRepos/azure-repos-request-helper';
 import { PullRequestState } from '@/shared/domain/enums/pullRequestState.enum';
 import { AzureGitPullRequestState } from '@/shared/domain/enums/pullRequestState.enum';
-import { Comment, FileChange } from '@/config/types/general/codeReview.type';
+import {
+    Comment,
+    CommentResult,
+    FileChange,
+} from '@/config/types/general/codeReview.type';
 import { Repository } from '@/config/types/general/codeReview.type';
 import { IRepositoryManager } from '@/core/domain/repository/contracts/repository-manager.contract';
 import { REPOSITORY_MANAGER_TOKEN } from '@/core/domain/repository/contracts/repository-manager.contract';
@@ -48,6 +53,7 @@ import { decrypt, encrypt } from '@/shared/utils/crypto';
 import { generateWebhookToken } from '@/shared/utils/webhooks/webhookTokenCrypto';
 import { ICodeManagementService } from '@/core/domain/platformIntegrations/interfaces/code-management.interface';
 import {
+    AzurePullRequestVote,
     AzureRepoPRThread,
     EventConfig,
 } from '@/core/domain/azureRepos/entities/azureRepoExtras.type';
@@ -75,17 +81,13 @@ export class AzureReposService
             | 'getPullRequestReviewThreads'
             | 'getListOfValidReviews'
             | 'getPullRequestsWithChangesRequested'
-            | 'createSingleIssueComment'
             | 'findTeamAndOrganizationIdByConfigKey'
             | 'createResponseToComment'
             | 'getAuthenticationOAuthToken'
             | 'countReactions'
             | 'getRepositoryAllFiles'
             | 'mergePullRequest'
-            | 'approvePullRequest'
-            | 'requestChangesPullRequest'
             | 'getUserById'
-            | 'markReviewCommentAsResolved'
         >
 {
     constructor(
@@ -105,20 +107,241 @@ export class AzureReposService
         private readonly azureReposRequestHelper: AzureReposRequestHelper,
     ) {}
 
+    async markReviewCommentAsResolved(params: {
+        organizationAndTeamData: OrganizationAndTeamData;
+        repository: { id: string; name: string };
+        prNumber: number;
+        commentId: number;
+    }): Promise<any | null> {
+        try {
+            const { organizationAndTeamData, repository, prNumber, commentId } =
+                params;
+
+            const { orgName, token } = await this.getAuthDetails(
+                organizationAndTeamData,
+            );
+
+            const projectId = await this.getProjectIdFromRepository(
+                organizationAndTeamData,
+                repository.id,
+            );
+
+            const response =
+                await this.azureReposRequestHelper.resolvePullRequestThread({
+                    orgName,
+                    token,
+                    projectId,
+                    repositoryId: repository.id,
+                    prId: prNumber,
+                    threadId: commentId,
+                });
+
+            this.logger.log({
+                message: `Marked thread ${commentId} as resolved in PR #${prNumber}`,
+                context: AzureReposService.name,
+                serviceName: 'AzureReposService markReviewCommentAsResolved',
+                metadata: { params },
+            });
+
+            return response;
+        } catch (error) {
+            this.logger.error({
+                message: `Failed to resolve review thread in PR #${params.prNumber}`,
+                context: AzureReposService.name,
+                serviceName: 'AzureReposService markReviewCommentAsResolved',
+                error,
+                metadata: { params },
+            });
+            return null;
+        }
+    }
+
+    async approvePullRequest(params: {
+        organizationAndTeamData: OrganizationAndTeamData;
+        prNumber: number;
+        repository: { id: string; name: string };
+    }): Promise<any> {
+        try {
+            const { organizationAndTeamData, prNumber, repository } = params;
+
+            const { orgName, token } = await this.getAuthDetails(
+                organizationAndTeamData,
+            );
+            const projectId = await this.getProjectIdFromRepository(
+                organizationAndTeamData,
+                repository.id,
+            );
+
+            const reviewerId =
+                await this.azureReposRequestHelper.getAuthenticatedUserId({
+                    orgName,
+                    token,
+                });
+
+            if (!reviewerId) {
+                throw new Error('Unable to identify reviewer from PAT.');
+            }
+
+            const result = await this.azureReposRequestHelper.votePullRequest({
+                orgName,
+                token,
+                projectId,
+                repositoryId: repository.id,
+                prId: prNumber,
+                reviewerId,
+                vote: AzurePullRequestVote.Approved, // approve
+            });
+
+            this.logger.log({
+                message: `Approved pull request #${prNumber}`,
+                context: AzureReposService.name,
+                serviceName: 'AzureReposService approvePullRequest',
+                metadata: { params },
+            });
+
+            return result;
+        } catch (error) {
+            this.logger.error({
+                message: `Error approving pull request #${params.prNumber}`,
+                context: AzureReposService.name,
+                serviceName: 'AzureReposService approvePullRequest',
+                error,
+                metadata: { params },
+            });
+            return null;
+        }
+    }
+
+    async requestChangesPullRequest(params: {
+        organizationAndTeamData: OrganizationAndTeamData;
+        prNumber: number;
+        repository: { id: string; name: string };
+        criticalComments: CommentResult[];
+    }): Promise<any> {
+        try {
+            const {
+                organizationAndTeamData,
+                prNumber,
+                repository,
+                criticalComments,
+            } = params;
+
+            const { orgName, token } = await this.getAuthDetails(
+                organizationAndTeamData,
+            );
+            const projectId = await this.getProjectIdFromRepository(
+                organizationAndTeamData,
+                repository.id,
+            );
+
+            const reviewerId =
+                await this.azureReposRequestHelper.getAuthenticatedUserId({
+                    orgName,
+                    token,
+                });
+
+            if (!reviewerId) {
+                throw new Error('Unable to identify reviewer from PAT.');
+            }
+
+            await this.azureReposRequestHelper.votePullRequest({
+                orgName,
+                token,
+                projectId,
+                repositoryId: repository.id,
+                prId: prNumber,
+                reviewerId,
+                vote: AzurePullRequestVote.Rejected, // request changes
+            });
+
+            const title =
+                '# Found critical issues, please review the requested changes';
+            const listOfCriticalIssues =
+                this.getListOfCriticalIssues(criticalComments);
+            const bodyFormatted = `${title}\n\n${listOfCriticalIssues}`;
+
+            await this.createSingleIssueComment({
+                body: bodyFormatted,
+                organizationAndTeamData,
+                prNumber,
+                repository,
+            });
+
+            this.logger.log({
+                message: `Requested changes on pull request #${prNumber}`,
+                context: AzureReposService.name,
+                serviceName: 'AzureReposService requestChangesPullRequest',
+                metadata: { params },
+            });
+
+            return true;
+        } catch (error) {
+            this.logger.error({
+                message: `Error requesting changes on pull request #${params.prNumber}`,
+                context: AzureReposService.name,
+                serviceName: 'AzureReposService requestChangesPullRequest',
+                error,
+                metadata: { params },
+            });
+            return null;
+        }
+    }
+
+    async createSingleIssueComment(params: {
+        organizationAndTeamData: OrganizationAndTeamData;
+        repository: { id: string };
+        prNumber: number;
+        body: string;
+    }): Promise<any | null> {
+        try {
+            const { organizationAndTeamData, repository, prNumber, body } =
+                params;
+
+            const { orgName, token } = await this.getAuthDetails(
+                organizationAndTeamData,
+            );
+
+            const projectId = await this.getProjectIdFromRepository(
+                organizationAndTeamData,
+                repository.id,
+            );
+
+            const response =
+                await this.azureReposRequestHelper.createIssueComment({
+                    orgName,
+                    token,
+                    projectId,
+                    repositoryId: repository.id,
+                    prId: prNumber,
+                    comment: body,
+                });
+
+            return response;
+        } catch (error) {
+            this.logger.error({
+                message: 'Failed to create single issue comment',
+                context: AzureReposService.name,
+                serviceName: 'AzureReposService createSingleIssueComment',
+                error,
+                metadata: { params },
+            });
+            return null;
+        }
+    }
+
     async getPullRequestReviewComment(params: {
         organizationAndTeamData: OrganizationAndTeamData;
         filters: {
             repository: { id: string };
             pullRequestNumber: number;
-            discussionId?: number;
         };
     }): Promise<any[] | null> {
-        const { organizationAndTeamData, filters } = params;
-
         try {
+            const { organizationAndTeamData, filters } = params;
             const { orgName, token } = await this.getAuthDetails(
                 organizationAndTeamData,
             );
+
             const projectId = await this.getProjectIdFromRepository(
                 organizationAndTeamData,
                 filters.repository.id,
@@ -133,35 +356,42 @@ export class AzureReposService
                     prId: filters.pullRequestNumber,
                 });
 
-            if (!filters.discussionId) {
-                return threads;
+            if (!threads?.length) {
+                return [];
             }
 
-            const thread = threads.find((t) => t.id === filters.discussionId);
-            if (!thread) return [];
-
-            const originalCommentBody = thread?.comments?.[0]?.content ?? null;
-
-            const mappedComments = (thread.comments ?? []).map((note) => ({
-                id: note.id,
-                body: note.content,
-                createdAt: note.publishedDate,
-                originalCommit: { body: originalCommentBody },
-                author: {
-                    id: note.author?.id,
-                    username: note.author?.displayName,
-                    name: note.author?.displayName,
-                },
-            }));
+            const comments = threads?.flatMap((thread) => {
+                const commitId =
+                    thread.pullRequestThreadContext?.commitId ?? null;
+
+                return (thread.comments ?? [])
+                    ?.filter((note) => !!note.content?.trim())
+                    ?.map((note) => ({
+                        id: note.id,
+                        threadId: thread.id,
+                        commentType: note?.commentType,
+                        body: note.content,
+                        createdAt: note.publishedDate,
+                        originalCommit: commitId,
+                        isResolved:
+                            thread?.status === 'closed' ||
+                            thread?.status === 'fixed',
+                        author: {
+                            id: note.author?.id,
+                            username: note.author?.uniqueName,
+                            name: note.author?.displayName,
+                        },
+                    }));
+            });
 
-            return mappedComments.sort(
+            return comments.sort(
                 (a, b) =>
                     new Date(b.createdAt).getTime() -
                     new Date(a.createdAt).getTime(),
             );
         } catch (error) {
             this.logger.error({
-                message: 'Failed to get pull request review comments',
+                message: 'Failed to get pull request review comment',
                 context: AzureReposService.name,
                 serviceName: 'AzureReposService getPullRequestReviewComment',
                 error,
@@ -196,20 +426,33 @@ export class AzureReposService
                     prId: prNumber,
                 });
 
-            const comments = threads.flatMap((thread) =>
-                (thread.comments ?? []).map((comment) => ({
-                    id: comment.id,
-                    body: comment.content,
-                    createdAt: comment.publishedDate,
-                    originalCommit:
-                        thread.pullRequestThreadContext?.commitId ?? null,
-                    author: {
-                        id: comment.author?.id,
-                        username: comment.author?.displayName,
-                        name: comment.author?.displayName,
-                    },
-                })),
-            );
+            if (!threads?.length) {
+                return [];
+            }
+
+            const comments = threads?.flatMap((thread) => {
+                const commitId =
+                    thread.pullRequestThreadContext?.commitId ?? null;
+
+                return (thread.comments ?? [])
+                    ?.filter((note) => !!note.content?.trim())
+                    ?.map((note) => ({
+                        id: note.id,
+                        threadId: thread.id,
+                        commentType: note?.commentType,
+                        body: note.content,
+                        createdAt: note.publishedDate,
+                        isResolved:
+                            thread?.status === 'closed' ||
+                            thread?.status === 'fixed',
+                        originalCommit: commitId,
+                        author: {
+                            id: note.author?.id,
+                            username: note.author?.uniqueName,
+                            name: note.author?.displayName,
+                        },
+                    }));
+            });
 
             return comments;
         } catch (error) {
@@ -665,6 +908,7 @@ export class AzureReposService
             return null;
         }
     }
+
     async updateIssueComment(params: {
         organizationAndTeamData: OrganizationAndTeamData;
         repository: { name: string; id: string; project: { id: string } };
@@ -779,6 +1023,7 @@ export class AzureReposService
 
         return defaultBranch;
     }
+
     async getPullRequestReviewComments(params: {
         organizationAndTeamData: OrganizationAndTeamData;
         repository: Partial<Repository>;
@@ -809,10 +1054,13 @@ export class AzureReposService
                     (thread.comments || []).map((comment) => ({
                         id: comment.id,
                         threadId: String(thread.id),
+                        commentType: comment?.commentType,
                         body: comment.content ?? '',
                         createdAt: comment.publishedDate,
                         updatedAt: comment.lastUpdatedDate,
-                        isResolved: thread.status === 'closed',
+                        isResolved:
+                            thread?.status === 'closed' ||
+                            thread?.status === 'fixed',
                         author: {
                             id: comment.author?.id,
                             username: comment.author?.displayName,
@@ -2551,4 +2799,35 @@ export class AzureReposService
             return iterationIndex > reviewedIndex;
         });
     }
+
+    private getListOfCriticalIssues(criticalComments: CommentResult[]): string {
+        const criticalIssuesSummaryArray =
+            this.getCriticalIssuesSummaryArray(criticalComments);
+
+        const listOfCriticalIssues = criticalIssuesSummaryArray
+            .map((criticalIssue) => {
+                const summary = criticalIssue.oneSentenceSummary;
+                const formattedItem = `- ${summary}`;
+
+                return formattedItem.trim();
+            })
+            .join('\n');
+
+        return listOfCriticalIssues;
+    }
+
+    private getCriticalIssuesSummaryArray(
+        criticalComments: CommentResult[],
+    ): OneSentenceSummaryItem[] {
+        const criticalIssuesSummaryArray: OneSentenceSummaryItem[] =
+            criticalComments.map((comment) => {
+                return {
+                    id: comment.codeReviewFeedbackData.commentId,
+                    oneSentenceSummary:
+                        comment.comment.suggestion.oneSentenceSummary ?? '',
+                };
+            });
+
+        return criticalIssuesSummaryArray;
+    }
 }
diff --git a/src/core/infrastructure/adapters/services/azureRepos/azure-repos-request-helper.ts b/src/core/infrastructure/adapters/services/azureRepos/azure-repos-request-helper.ts
index dc90de150..f55ad79ee 100644
--- a/src/core/infrastructure/adapters/services/azureRepos/azure-repos-request-helper.ts
+++ b/src/core/infrastructure/adapters/services/azureRepos/azure-repos-request-helper.ts
@@ -8,7 +8,6 @@ import {
     AzureRepoChange,
     AzureRepoCommit,
     AzureRepoFileContent,
-    AzureRepoDiffChange,
     AzureRepoPRThread,
     AzureRepoSubscription,
     AzureRepoCommentType,
@@ -18,7 +17,7 @@ import { FileChange } from '@/config/types/general/codeReview.type';
 
 @Injectable()
 export class AzureReposRequestHelper {
-    constructor() { }
+    constructor() {}
 
     async getProjects(params: {
         orgName: string;
@@ -130,7 +129,8 @@ export class AzureReposRequestHelper {
     }): Promise<AzureRepoPRThread> {
         const instance = await this.azureRequest(params);
 
-        const isMultiLine = params.start_line !== undefined && params.start_line !== null;
+        const isMultiLine =
+            params.start_line !== undefined && params.start_line !== null;
 
         const payload = {
             comments: [
@@ -143,7 +143,10 @@ export class AzureReposRequestHelper {
             threadContext: {
                 filePath: params.filePath,
                 rightFileStart: {
-                    line: Math.max(isMultiLine ? params.start_line! : params.line, 1),
+                    line: Math.max(
+                        isMultiLine ? params.start_line! : params.line,
+                        1,
+                    ),
                     offset: 1,
                 },
                 rightFileEnd: {
@@ -302,6 +305,25 @@ export class AzureReposRequestHelper {
         return data;
     }
 
+    async getAuthenticatedUserId(params: {
+        orgName: string;
+        token: string;
+    }): Promise<string | null> {
+        const { orgName, token } = params;
+
+        const instance = await this.azureRequest({
+            orgName,
+            token,
+            useGraphApi: true,
+        });
+
+        const { data } = await instance.get(
+            '/_apis/connectionData?connectOptions=IncludeServices&api-version=7.1-preview',
+        );
+
+        return data?.authenticatedUser?.id;
+    }
+
     private async azureRequest({
         orgName,
         token,
@@ -326,6 +348,25 @@ export class AzureReposRequestHelper {
         return instance;
     }
 
+    async resolvePullRequestThread(params: {
+        orgName: string;
+        token: string;
+        projectId: string;
+        repositoryId: string;
+        prId: number;
+        threadId: number;
+    }): Promise<any> {
+        const instance = await this.azureRequest(params);
+
+        const url = `/${params.projectId}/_apis/git/repositories/${params.repositoryId}/pullRequests/${params.prId}/threads/${params.threadId}?api-version=7.1`;
+
+        const payload = { status: 'closed' };
+
+        const { data } = await instance.patch(url, payload);
+
+        return data;
+    }
+
     async getIterations(params: {
         orgName: string;
         token: string;
@@ -573,6 +614,25 @@ export class AzureReposRequestHelper {
         return data;
     }
 
+    async votePullRequest(params: {
+        orgName: string;
+        token: string;
+        projectId: string;
+        repositoryId: string;
+        prId: number;
+        reviewerId: string;
+        vote: number; // 10 = approve, -10 = reject/request changes
+    }): Promise<any> {
+        const instance = await this.azureRequest(params);
+
+        const url = `/${params.projectId}/_apis/git/repositories/${params.repositoryId}/pullRequests/${params.prId}/reviewers/${params.reviewerId}?api-version=7.1`;
+
+        const payload = { vote: params.vote };
+
+        const { data } = await instance.put(url, payload);
+        return data;
+    }
+
     async getRepositoryContentFile(params: {
         orgName: string;
         token: string;
diff --git a/src/core/infrastructure/adapters/services/bitbucket/bitbucket.service.ts b/src/core/infrastructure/adapters/services/bitbucket/bitbucket.service.ts
index 326a18388..1093641da 100644
--- a/src/core/infrastructure/adapters/services/bitbucket/bitbucket.service.ts
+++ b/src/core/infrastructure/adapters/services/bitbucket/bitbucket.service.ts
@@ -2,8 +2,6 @@ import { OrganizationAndTeamData } from '@/config/types/general/organizationAndT
 import { IBitbucketService } from '@/core/domain/bitbucket/contracts/bitbucket.service.contract';
 import { IntegrationConfigEntity } from '@/core/domain/integrationConfigs/entities/integration-config.entity';
 import { ICodeManagementService } from '@/core/domain/platformIntegrations/interfaces/code-management.interface';
-import { CommitLeadTimeForChange } from '@/core/domain/platformIntegrations/types/codeManagement/commitLeadTimeForChange.type';
-import { DeployFrequency } from '@/core/domain/platformIntegrations/types/codeManagement/deployFrequency.type';
 import {
     PullRequests,
     PullRequestWithFiles,
@@ -70,11 +68,21 @@ import { IRepository } from '@/core/domain/pullRequests/interfaces/pullRequests.
 @IntegrationServiceDecorator(PlatformType.BITBUCKET, 'codeManagement')
 export class BitbucketService
     implements
-    IBitbucketService,
-    Omit<
-        ICodeManagementService,
-        'getOrganizations' | 'getListOfValidReviews' | 'getUserByEmailOrName'
-    > {
+        IBitbucketService,
+        Omit<
+            ICodeManagementService,
+            | 'getOrganizations'
+            | 'getListOfValidReviews'
+            | 'getUserByEmailOrName'
+            | 'getPullRequestReviewThreads'
+            | 'getUserById'
+            | 'getDataForCalculateDeployFrequency'
+            | 'getCommitsByReleaseMode'
+            | 'getAuthenticationOAuthToken'
+            | 'countReactions'
+            | 'getRepositoryAllFiles'
+        >
+{
     constructor(
         @Inject(INTEGRATION_SERVICE_TOKEN)
         private readonly integrationService: IIntegrationService,
@@ -94,7 +102,7 @@ export class BitbucketService
         private readonly promptService: PromptService,
 
         private readonly logger: PinoLoggerService,
-    ) { }
+    ) {}
 
     async getPullRequestsWithChangesRequested(params: {
         organizationAndTeamData: OrganizationAndTeamData;
@@ -155,22 +163,6 @@ export class BitbucketService
         }
     }
 
-    // Only relevant for github
-    getPullRequestReviewThreads(params: {
-        organizationAndTeamData: OrganizationAndTeamData;
-        repository: Partial<Repository>;
-        prNumber: number;
-    }): Promise<any | null> {
-        throw new Error('Method not implemented.');
-    }
-
-    getUserById(params: {
-        organizationAndTeamData: OrganizationAndTeamData;
-        userId: string;
-    }): Promise<any | null> {
-        throw new Error('Method not implemented.');
-    }
-
     async cloneRepository(params: {
         repository: Pick<
             Repository,
@@ -945,25 +937,6 @@ export class BitbucketService
         }
     }
 
-    getDataForCalculateDeployFrequency(
-        params: any,
-    ): Promise<DeployFrequency[]> {
-        this.logger.error({
-            message: 'Method not implemented.',
-            context: BitbucketService.name,
-            serviceName: 'BitbucketService getDataForCalculateDeployFrequency',
-        });
-        throw new Error('Method not implemented.');
-    }
-    getCommitsByReleaseMode(params: any): Promise<CommitLeadTimeForChange[]> {
-        this.logger.error({
-            message: 'Method not implemented.',
-            context: BitbucketService.name,
-            serviceName: 'BitbucketService getCommitsByReleaseMode',
-        });
-        throw new Error('Method not implemented.');
-    }
-
     async getPullRequestsWithFiles(params: {
         organizationAndTeamData: OrganizationAndTeamData;
         filters?: any;
@@ -1337,50 +1310,74 @@ export class BitbucketService
         prNumber: number;
         lastCommit: any;
     }): Promise<FileChange[] | null> {
-        const { organizationAndTeamData, repository, prNumber, lastCommit } = params;
+        const { organizationAndTeamData, repository, prNumber, lastCommit } =
+            params;
 
         try {
-            const bitbucketAuthDetails = await this.getAuthDetails(organizationAndTeamData);
+            const bitbucketAuthDetails = await this.getAuthDetails(
+                organizationAndTeamData,
+            );
             if (!bitbucketAuthDetails) return null;
 
-            const repo = await this.getRepoById(organizationAndTeamData, repository.id);
-            const bitbucketAPI = this.instanceBitbucketApi(bitbucketAuthDetails);
+            const repo = await this.getRepoById(
+                organizationAndTeamData,
+                repository.id,
+            );
+            const bitbucketAPI =
+                this.instanceBitbucketApi(bitbucketAuthDetails);
 
             // 🔍 Pega o estado atual do PR
-            const pr = await bitbucketAPI.pullrequests.get({
-                pull_request_id: prNumber,
-                repo_slug: `{${repo.id}}`,
-                workspace: `{${repo.workspaceId}}`,
-            }).then(res => res.data);
+            const pr = await bitbucketAPI.pullrequests
+                .get({
+                    pull_request_id: prNumber,
+                    repo_slug: `{${repo.id}}`,
+                    workspace: `{${repo.workspaceId}}`,
+                })
+                .then((res) => res.data);
 
             // 📄 Lista todos os arquivos tocados no PR até agora
-            const allFilesInPR = await bitbucketAPI.pullrequests.getDiffStat({
-                pull_request_id: prNumber,
-                repo_slug: `{${repo.id}}`,
-                workspace: `{${repo.workspaceId}}`,
-            }).then(res => this.getPaginatedResults<Schema.Diffstat>(bitbucketAPI, res));
+            const allFilesInPR = await bitbucketAPI.pullrequests
+                .getDiffStat({
+                    pull_request_id: prNumber,
+                    repo_slug: `{${repo.id}}`,
+                    workspace: `{${repo.workspaceId}}`,
+                })
+                .then((res) =>
+                    this.getPaginatedResults<Schema.Diffstat>(
+                        bitbucketAPI,
+                        res,
+                    ),
+                );
 
             // ⚙️ Processa apenas arquivos que realmente mudaram desde o último commit
             const changedFiles = await Promise.all(
                 allFilesInPR.map(async (file) => {
                     const path = file.new?.path;
-                    if (!path) return null;
+                    if (!path) {
+                        return null;
+                    }
 
-                    const diff = await bitbucketAPI.commits.getDiff({
-                        repo_slug: `{${repo.id}}`,
-                        workspace: `{${repo.workspaceId}}`,
-                        spec: `${pr.source?.commit?.hash}..${lastCommit.sha}`,
-                        path,
-                    }).then(res => res.data as string);
+                    const diff = await bitbucketAPI.commits
+                        .getDiff({
+                            repo_slug: `{${repo.id}}`,
+                            workspace: `{${repo.workspaceId}}`,
+                            spec: `${pr.source?.commit?.hash}..${lastCommit.sha}`,
+                            path,
+                        })
+                        .then((res) => res.data as string);
 
-                    if (!diff?.trim()) return null;
+                    if (!diff?.trim()) {
+                        return null;
+                    }
 
-                    const content = await bitbucketAPI.source.read({
-                        repo_slug: `{${repo.id}}`,
-                        workspace: `{${repo.workspaceId}}`,
-                        commit: pr.source?.commit?.hash,
-                        path,
-                    }).then(res => res.data as string);
+                    const content = await bitbucketAPI.source
+                        .read({
+                            repo_slug: `{${repo.id}}`,
+                            workspace: `{${repo.workspaceId}}`,
+                            commit: pr.source?.commit?.hash,
+                            path,
+                        })
+                        .then((res) => res.data as string);
 
                     return {
                         filename: path,
@@ -1395,13 +1392,14 @@ export class BitbucketService
                         contents_url: null,
                         raw_url: null,
                     };
-                })
+                }),
             );
 
             return changedFiles.filter(Boolean);
         } catch (error) {
             this.logger.error({
-                message: 'Error to get incremental changed files since last commit',
+                message:
+                    'Error to get incremental changed files since last commit',
                 context: BitbucketService.name,
                 serviceName: 'getIncrementalChangedFilesSinceLastCommit',
                 error,
@@ -1475,7 +1473,7 @@ export class BitbucketService
                             path: lineComment?.path,
                             to: this.sanitizeLine(
                                 params.lineComment.start_line ??
-                                params.lineComment.line,
+                                    params.lineComment.line,
                             ),
                         },
                     },
@@ -1667,19 +1665,6 @@ export class BitbucketService
         }
     }
 
-    async indexRepositoriesForCodeReview(params: {
-        organizationAndTeamData: OrganizationAndTeamData;
-        configValue: any;
-        integrationId: string;
-    }): Promise<void> {
-        this.logger.error({
-            message: 'Method not implemented.',
-            context: BitbucketService.name,
-            serviceName: 'BitbucketService indexRepositoriesForCodeReview',
-        });
-        throw new Error('Method not implemented.');
-    }
-
     async getCommitsForPullRequestForCodeReview(params: {
         organizationAndTeamData: OrganizationAndTeamData;
         repository: { name: string; id: string };
@@ -1815,13 +1800,72 @@ export class BitbucketService
         }
     }
 
-    createSingleIssueComment(params: any): Promise<any | null> {
-        this.logger.error({
-            message: 'Method not implemented.',
-            context: BitbucketService.name,
-            serviceName: 'BitbucketService createSingleIssueComment',
-        });
-        throw new Error('Method not implemented.');
+    async createSingleIssueComment(params: {
+        organizationAndTeamData: OrganizationAndTeamData;
+        repository: { name: string; id: string };
+        prNumber: number;
+        body: string;
+    }): Promise<any | null> {
+        const { organizationAndTeamData, repository, prNumber, body } = params;
+
+        try {
+            const bitbucketAuthDetails = await this.getAuthDetails(
+                organizationAndTeamData,
+            );
+            if (!bitbucketAuthDetails) return null;
+
+            const repo = await this.getRepoById(
+                organizationAndTeamData,
+                repository.id,
+            );
+            const bitbucketAPI =
+                this.instanceBitbucketApi(bitbucketAuthDetails);
+
+            const response = await bitbucketAPI.pullrequests.createComment({
+                pull_request_id: prNumber,
+                repo_slug: `{${repo.id}}`,
+                workspace: `{${repo.workspaceId}}`,
+                // @ts-ignore
+                _body: {
+                    content: {
+                        raw: body,
+                    },
+                },
+            });
+
+            const commentData = response?.data;
+
+            if (!commentData?.id) {
+                throw new Error(`Failed to create comment in PR#${prNumber}`);
+            }
+
+            this.logger.log({
+                message: `Created issue comment for PR#${prNumber}`,
+                context: this.createSingleIssueComment.name,
+                metadata: { params },
+            });
+
+            return {
+                id: commentData.id,
+                threadId: commentData.id,
+                content: commentData.content?.raw,
+                createdAt: commentData.created_on,
+                author: {
+                    id: commentData.user?.uuid,
+                    username: commentData.user?.nickname,
+                    name: commentData.user?.display_name,
+                },
+            };
+        } catch (error) {
+            this.logger.error({
+                message: 'Error creating single issue comment on Bitbucket',
+                context: this.createSingleIssueComment.name,
+                error,
+                metadata: { params },
+            });
+
+            return null;
+        }
     }
 
     async updateIssueComment(params: {
@@ -2171,23 +2215,6 @@ export class BitbucketService
         }
     }
 
-    getAuthenticationOAuthToken(params: any): Promise<string> {
-        this.logger.error({
-            message: 'Method not implemented.',
-            context: BitbucketService.name,
-            serviceName: 'BitbucketService getAuthenticationOAuthToken',
-        });
-        throw new Error('Method not implemented.');
-    }
-    countReactions(params: any): Promise<any[]> {
-        this.logger.error({
-            message: 'Method not implemented.',
-            context: BitbucketService.name,
-            serviceName: 'BitbucketService countReactions',
-        });
-        throw new Error('Method not implemented.');
-    }
-
     async getLanguageRepository(params: {
         organizationAndTeamData: OrganizationAndTeamData;
         repository: { name: string; id: string };
@@ -2229,15 +2256,6 @@ export class BitbucketService
         }
     }
 
-    getRepositoryAllFiles(params: any): Promise<any> {
-        this.logger.error({
-            message: 'Method not implemented.',
-            context: BitbucketService.name,
-            serviceName: 'BitbucketService getRepositoryAllFiles',
-        });
-        throw new Error('Method not implemented.');
-    }
-
     async createAuthIntegration(params: {
         organizationAndTeamData: OrganizationAndTeamData;
         authMode: AuthMode;
@@ -2541,19 +2559,6 @@ export class BitbucketService
         }
     }
 
-    async authenticateWithCodeOauth(params: {
-        organizationAndTeamData: OrganizationAndTeamData;
-        authMode: AuthMode;
-        code: string;
-    }): Promise<{ success: boolean }> {
-        this.logger.error({
-            message: 'Method not implemented.',
-            context: BitbucketService.name,
-            serviceName: 'BitbucketService authenticateWithCodeOauth',
-        });
-        throw new Error('Method not implemented.');
-    }
-
     async authenticateWithToken(params: {
         organizationAndTeamData: OrganizationAndTeamData;
         username: string;
@@ -3050,8 +3055,9 @@ export class BitbucketService
                 queryString += `created_on >= "${filters.startDate}"`;
             }
             if (filters?.endDate) {
-                queryString += `${queryString ? ' AND ' : ''
-                    }created_on <= "${filters.endDate}"`;
+                queryString += `${
+                    queryString ? ' AND ' : ''
+                }created_on <= "${filters.endDate}"`;
             }
 
             const pullRequests = await bitbucketAPI.pullrequests
diff --git a/src/core/infrastructure/adapters/services/codeBase/codeReviewPipeline/stages/process-files-review.stage.ts b/src/core/infrastructure/adapters/services/codeBase/codeReviewPipeline/stages/process-files-review.stage.ts
index d5fc48379..b23934fa3 100644
--- a/src/core/infrastructure/adapters/services/codeBase/codeReviewPipeline/stages/process-files-review.stage.ts
+++ b/src/core/infrastructure/adapters/services/codeBase/codeReviewPipeline/stages/process-files-review.stage.ts
@@ -1085,7 +1085,7 @@ export class ProcessFilesReview extends BasePipelineStage<CodeReviewPipelineCont
                 implementedSuggestionsCommentIds.includes(comment.id),
             );
 
-        if (foundComments.length > 0) {
+        if (foundComments?.length > 0) {
             const promises = foundComments.map(
                 async (foundComment: PullRequestReviewComment) => {
                     let commentId =
diff --git a/src/core/infrastructure/adapters/services/codeBase/commentAnalysis.service.ts b/src/core/infrastructure/adapters/services/codeBase/commentAnalysis.service.ts
index 5e94f8627..2b7b67148 100644
--- a/src/core/infrastructure/adapters/services/codeBase/commentAnalysis.service.ts
+++ b/src/core/infrastructure/adapters/services/codeBase/commentAnalysis.service.ts
@@ -544,6 +544,14 @@ export class CommentAnalysisService {
                             body: note.body,
                         }));
                     }
+
+                    if (comment?.threadId) {
+                        // Azure DevOps: ensure unique ID
+                        return {
+                            ...comment,
+                            id: `${comment.threadId}-${comment.id}`, // composite ID
+                        };
+                    }
                     return comment;
                 });
 
@@ -558,19 +566,19 @@ export class CommentAnalysisService {
                 }
 
                 const filteredComments = uniqueComments
-                    .filter(
+                    ?.filter(
                         (comment) =>
-                            !comment.user ||
-                            !comment.user.type ||
-                            comment.user.type.toLowerCase() !== 'bot',
+                            !comment?.user ||
+                            !comment?.user?.type ||
+                            comment?.user?.type?.toLowerCase() !== 'bot',
                     )
-                    .filter(
+                    ?.filter(
                         (comment) =>
-                            !comment.body
-                                .toLowerCase()
-                                .includes('kody-codereview'),
+                            !comment?.body
+                                ?.toLowerCase()
+                                ?.includes('kody-codereview'),
                     )
-                    .filter((comment) => comment.body.length > 100);
+                    ?.filter((comment) => comment?.body?.length > 100);
 
                 let finalComments = filteredComments;
                 if (pr.files && pr.files.length > 0) {
diff --git a/src/core/infrastructure/adapters/services/cron/CheckIfPRCanBeApproved.cron.ts b/src/core/infrastructure/adapters/services/cron/CheckIfPRCanBeApproved.cron.ts
index 882b1b98d..030a4cc06 100644
--- a/src/core/infrastructure/adapters/services/cron/CheckIfPRCanBeApproved.cron.ts
+++ b/src/core/infrastructure/adapters/services/cron/CheckIfPRCanBeApproved.cron.ts
@@ -14,15 +14,26 @@ import { IntegrationStatusFilter } from '@/core/domain/team/interfaces/team.inte
 import { STATUS } from '@/config/types/database/status.type';
 import { ParametersKey } from '@/shared/domain/enums/parameters-key.enum';
 import { PullRequestsEntity } from '@/core/domain/pullRequests/entities/pullRequests.entity';
-import { IPullRequestsService, PULL_REQUESTS_SERVICE_TOKEN } from '@/core/domain/pullRequests/contracts/pullRequests.service.contracts';
+import {
+    IPullRequestsService,
+    PULL_REQUESTS_SERVICE_TOKEN,
+} from '@/core/domain/pullRequests/contracts/pullRequests.service.contracts';
 import { CodeManagementService } from '../platformIntegration/codeManagement.service';
 import { OrganizationAndTeamData } from '@/config/types/general/organizationAndTeamData';
 import { PlatformType } from '@/shared/domain/enums/platform-type.enum';
-import { CodeReviewConfig, CodeReviewConfigWithRepositoryInfo, Repository } from '@/config/types/general/codeReview.type';
+import {
+    CodeReviewConfig,
+    CodeReviewConfigWithRepositoryInfo,
+} from '@/config/types/general/codeReview.type';
 import { PullRequestState } from '@/shared/domain/enums/pullRequestState.enum';
-import { IIntegrationService, INTEGRATION_SERVICE_TOKEN } from '@/core/domain/integrations/contracts/integration.service.contracts';
+import {
+    IIntegrationService,
+    INTEGRATION_SERVICE_TOKEN,
+} from '@/core/domain/integrations/contracts/integration.service.contracts';
+import { AzureRepoCommentTypeString } from '@/core/domain/azureRepos/entities/azureRepoExtras.type';
 
-const API_CRON_CHECK_IF_PR_SHOULD_BE_APPROVED = process.env.API_CRON_CHECK_IF_PR_SHOULD_BE_APPROVED;
+const API_CRON_CHECK_IF_PR_SHOULD_BE_APPROVED =
+    process.env.API_CRON_CHECK_IF_PR_SHOULD_BE_APPROVED;
 
 @Injectable()
 export class CheckIfPRCanBeApprovedCronProvider {
@@ -42,7 +53,7 @@ export class CheckIfPRCanBeApprovedCronProvider {
         private readonly integrationService: IIntegrationService,
 
         private readonly codeManagementService: CodeManagementService,
-    ) { }
+    ) {}
 
     @Cron(API_CRON_CHECK_IF_PR_SHOULD_BE_APPROVED, {
         name: 'CHECK IF PR SHOULD BE APPROVED',
@@ -82,15 +93,16 @@ export class CheckIfPRCanBeApprovedCronProvider {
 
                 const organizationAndTeamData: OrganizationAndTeamData = {
                     organizationId,
-                    teamId
-                }
+                    teamId,
+                };
 
-                const codeReviewParameter = await this.parametersService.findByKey(
-                    ParametersKey.CODE_REVIEW_CONFIG,
-                    organizationAndTeamData,
-                );
+                const codeReviewParameter =
+                    await this.parametersService.findByKey(
+                        ParametersKey.CODE_REVIEW_CONFIG,
+                        organizationAndTeamData,
+                    );
 
-                if (!codeReviewParameter || !codeReviewParameter.configValue) {
+                if (!codeReviewParameter || !codeReviewParameter?.configValue) {
                     this.logger.error({
                         message: 'Code review parameter configs not found',
                         context: CheckIfPRCanBeApprovedCronProvider.name,
@@ -103,14 +115,20 @@ export class CheckIfPRCanBeApprovedCronProvider {
                     continue;
                 }
 
-                const codeReviewConfig = codeReviewParameter.configValue as {
-                    global: CodeReviewConfig
-                    repositories: CodeReviewConfigWithRepositoryInfo[]
-                }
-
-                if (!Array.isArray(codeReviewConfig.repositories) || codeReviewConfig.repositories.length < 1) {
+                const codeReviewConfig = codeReviewParameter?.configValue as {
+                    global: CodeReviewConfig;
+                    repositories: CodeReviewConfigWithRepositoryInfo[];
+                };
+
+                if (
+                    !codeReviewParameter ||
+                    !codeReviewConfig ||
+                    !Array.isArray(codeReviewConfig.repositories) ||
+                    codeReviewConfig.repositories.length < 1
+                ) {
                     this.logger.error({
-                        message: 'No repositories were found on the code review parameter config value',
+                        message:
+                            'No repositories were found on the code review parameter config value',
                         context: CheckIfPRCanBeApprovedCronProvider.name,
                         metadata: {
                             organizationAndTeamData,
@@ -123,78 +141,41 @@ export class CheckIfPRCanBeApprovedCronProvider {
 
                 const openPullRequests = await this.pullRequestService.find({
                     status: PullRequestState.OPENED,
-                    organizationId: organizationId
-                })
+                    organizationId: organizationId,
+                });
 
-                if (!openPullRequests || openPullRequests.length === 0) {
+                if (!openPullRequests || openPullRequests?.length === 0) {
                     continue;
                 }
 
                 openPullRequests.map(async (pr) => {
                     const repository = pr.repository;
 
-                    const codeReviewConfigFromRepo = codeReviewConfig.repositories.find((codeReviewConfigRepo) => codeReviewConfigRepo.id === repository.id)
+                    const codeReviewConfigFromRepo =
+                        codeReviewConfig?.repositories?.find(
+                            (codeReviewConfigRepo) =>
+                                codeReviewConfigRepo.id === repository.id,
+                        );
 
-                    if (!codeReviewConfig.global.pullRequestApprovalActive && !codeReviewConfigFromRepo?.pullRequestApprovalActive) {
+                    if (
+                        !codeReviewConfig?.global?.pullRequestApprovalActive &&
+                        !codeReviewConfigFromRepo?.pullRequestApprovalActive
+                    ) {
                         return;
                     }
 
-                    if (codeReviewConfigFromRepo?.pullRequestApprovalActive === false) {
+                    if (
+                        codeReviewConfigFromRepo?.pullRequestApprovalActive ===
+                        false
+                    ) {
                         return;
                     }
 
-
                     await this.shouldApprovePR({
                         organizationAndTeamData,
                         pr,
-                    })
+                    });
                 });
-
-                // let prsWithChangesRequested: PullRequestsWithChangesRequested[];
-
-
-                // prsWithChangesRequested = await Promise.all(
-                //     codeReviewConfig.repositories.map(async (repository) => {
-                //         if (!codeReviewConfig.global.isRequestChangesActive && !repository?.isRequestChangesActive) {
-                //             return;
-                //         }
-
-
-                //         const prsWithRequest = await this.codeManagementService.getPullRequestsWithChangesRequested({
-                //             organizationAndTeamData,
-                //             repository: repository,
-                //         }, platformType);
-
-                //         return prsWithRequest;
-                //     })
-                // ).then(results => results.flat())
-                //     .catch((error) => {
-                //         this.logger.error({
-                //             message: 'Error fetching pull requests with changes requested for some repositories',
-                //             context: CheckIfPRCanBeApprovedCronProvider.name, error
-                //         }
-                //         );
-                //         return [];
-                //     });
-
-                // if (!prsWithChangesRequested || prsWithChangesRequested.length === 0) {
-                //     continue;
-                // }
-
-                // const openPullRequestsWithRequestedChanges = openPullRequests.filter((pr) =>
-                //     prsWithChangesRequested.map((prWithChangesRequested) => prWithChangesRequested.number).includes(pr.number)
-                // )
-
-                // if (!openPullRequestsWithRequestedChanges || openPullRequestsWithRequestedChanges.length === 0) {
-                //     continue;
-                // }
-
-                // openPullRequestsWithRequestedChanges.map(async (pr) => {
-                //     await this.shouldApprovePR({
-                //         organizationAndTeamData,
-                //         pr,
-                //     })
-                // });
             }
         } catch (error) {
             this.logger.error({
@@ -212,8 +193,8 @@ export class CheckIfPRCanBeApprovedCronProvider {
         organizationAndTeamData,
         pr,
     }: {
-        organizationAndTeamData: OrganizationAndTeamData,
-        pr: PullRequestsEntity,
+        organizationAndTeamData: OrganizationAndTeamData;
+        pr: PullRequestsEntity;
     }): Promise<boolean> {
         const repository = pr.repository;
         const prNumber = pr.number;
@@ -226,86 +207,61 @@ export class CheckIfPRCanBeApprovedCronProvider {
                 name: repository.name,
             },
             prNumber: prNumber,
-        }
+        };
         try {
-            let isPlatformTypeGithub: boolean = platformType === PlatformType.GITHUB;
+            let isPlatformTypeGithub: boolean =
+                platformType === PlatformType.GITHUB;
 
             let reviewComments: any[];
             if (isPlatformTypeGithub) {
-                reviewComments = await this.codeManagementService.getPullRequestReviewThreads(codeManagementRequestData, PlatformType.GITHUB);
-            }
-            else {
-                reviewComments = await this.codeManagementService.getPullRequestReviewComments(codeManagementRequestData, platformType);
+                reviewComments =
+                    await this.codeManagementService.getPullRequestReviewThreads(
+                        codeManagementRequestData,
+                        PlatformType.GITHUB,
+                    );
+            } else {
+                reviewComments =
+                    await this.codeManagementService.getPullRequestReviewComments(
+                        codeManagementRequestData,
+                        platformType,
+                    );
             }
 
-            // if (platformType === PlatformType.BITBUCKET) {
-            //     await this.getValidUserReviews({ organizationAndTeamData, prNumber, repository, reviewComments });
-            //     return true;
-            // }
-
             if (!reviewComments || reviewComments.length < 1) {
                 return false;
             }
 
-            const isEveryReviewCommentResolved = reviewComments?.every((reviewComment) => reviewComment.isResolved);
-
+            let isEveryReviewCommentResolved = null;
+
+            if (platformType === PlatformType.AZURE_REPOS) {
+                isEveryReviewCommentResolved = reviewComments
+                    .filter(
+                        (comment) =>
+                            comment?.commentType ===
+                            AzureRepoCommentTypeString.CODE,
+                    )
+                    ?.every((reviewComment) => reviewComment.isResolved);
+            } else {
+                isEveryReviewCommentResolved = reviewComments?.every(
+                    (reviewComment) => reviewComment.isResolved,
+                );
+            }
 
             if (isEveryReviewCommentResolved) {
-                await this.codeManagementService.approvePullRequest({
-                    organizationAndTeamData,
-                    prNumber,
-                    repository: {
-                        name: repository.name,
-                        id: repository.id,
-                    }
-                }, platformType);
+                await this.codeManagementService.approvePullRequest(
+                    {
+                        organizationAndTeamData,
+                        prNumber,
+                        repository: {
+                            name: repository.name,
+                            id: repository.id,
+                        },
+                    },
+                    platformType,
+                );
                 return true;
             }
-
-            // if (platformType === PlatformType.GITLAB) {
-            //     return false;
-            // }
-
-            // let criticalSuggestions = this.getCriticalSuggestions(pr);
-
-            // const foundComments: PullRequestReviewComment[] = isPlatformTypeGithub
-            //     ? reviewComments.filter((comment) =>
-            //         criticalSuggestions.map(c => c.comment.id).includes(Number(comment.fullDatabaseId))
-            //     )
-            //     : reviewComments.filter((comment) =>
-            //         criticalSuggestions.map(c => c.comment.id).includes(comment.id)
-            //     );
-
-            // const resolvedComments = foundComments.filter((comment) => comment.isResolved == true)
-
-            // if (resolvedComments.length < criticalSuggestions.length) {
-            //     return false;
-            // }
-
-            // // Github has a different route where the reviews of different types are registered
-            // // So we need to get all of them, filter so we get the ones that requested_changed and check if they're resolved.
-            // if (isPlatformTypeGithub) {
-            //     const validReviews = await this.codeManagementService.getListOfValidReviews(codeManagementRequestData, platformType);
-
-            //     const unresolvedReviews = validReviews.filter((review) => review.isResolved === false);
-
-            //     if (unresolvedReviews.length < 1) {
-            //         await this.codeManagementService.approvePullRequest(codeManagementRequestData, platformType);
-            //         return true;
-            //     }
-            //     return false;
-            // }
-            // else if (platformType === PlatformType.BITBUCKET) {
-            //     /**
-            //      * Each time someone requests a change, they appear as a reviewer on the PR (except kody, dunno why)
-            //      * We can use the reviewers information to filter the comments arrays that were made by other users besides kody.
-            //      * That should return to us a list of reviews specifically made by users. We can use this to check if the PR should be approved.
-            //     */
-            //     await this.getValidUserReviews({ organizationAndTeamData, prNumber, repository, reviewComments });
-
-            // }
-        }
-        catch (error) {
+        } catch (error) {
             this.logger.error({
                 message: 'Error in shouldApprovePR',
                 context: CheckIfPRCanBeApprovedCronProvider.name,
@@ -316,108 +272,12 @@ export class CheckIfPRCanBeApprovedCronProvider {
                     repository: {
                         name: repository.name,
                         id: repository.id,
-                    }
+                    },
                 },
-                error
+                error,
             });
 
             return false;
         }
     }
-
-    private async getValidUserReviews(params: {
-        repository: Partial<Repository>,
-        prNumber: number,
-        organizationAndTeamData: OrganizationAndTeamData,
-        reviewComments: any[]
-    }): Promise<boolean> {
-        const { organizationAndTeamData, prNumber, repository, reviewComments } = params;
-
-        const pr: any = await this.codeManagementService.getPullRequestDetails({
-            organizationAndTeamData, prNumber, repository: {
-                id: repository.id,
-                name: repository.name,
-            }
-        }, PlatformType.BITBUCKET);
-
-        const kodyUser = reviewComments.find((reviewComment) => {
-            return reviewComment.body && (reviewComment.body.includes('kody|code-review') || reviewComment.body.includes('![kody code-review]'));
-        });
-
-        const reviewers = kodyUser
-            ? pr.participants.filter((participant) => participant.id !== kodyUser?.author.id)
-            : pr.participants;
-
-
-        const isEveryReviewCommentResolved = reviewComments?.every((reviewComment) => reviewComment.isResolved);
-
-        if (isEveryReviewCommentResolved) {
-            await this.codeManagementService.approvePullRequest({
-                organizationAndTeamData,
-                prNumber,
-                repository: {
-                    name: repository.name,
-                    id: repository.id,
-                }
-            }, PlatformType.BITBUCKET);
-            return true;
-        }
-        // const kodyReviewer = kodyUser
-        //     ? pr.participants.find((participant) => participant.id === kodyUser?.author.id)
-        //     : null;
-
-        // if (kodyReviewer && kodyReviewer?.approved) {
-        //     return true;
-        // }
-
-        // const anyReviewerApproved = reviewers.some((reviewer) => reviewer.approved);
-
-        // if (anyReviewerApproved) {
-        //     return true;
-        // }
-
-        // const validReviews = reviewComments.filter((reviewComment) => {
-        //     return reviewers.some((reviewer) => reviewer.id === reviewComment.author.id);
-        // });
-
-        // const unresolvedReviews = validReviews.filter((review) => review.isResolved === false);
-
-        // if (unresolvedReviews.length < 1) {
-        //     await this.codeManagementService.approvePullRequest({
-        //         organizationAndTeamData,
-        //         prNumber,
-        //         repository: {
-        //             name: repository.name,
-        //             id: repository.id,
-        //         }
-        //     }, PlatformType.BITBUCKET);
-        //     return true;
-        // }
-        // return false;
-
-    }
-
-    // private getCriticalSuggestions(pr: PullRequestsEntity): CodeSuggestion[] {
-    //     const implementedSuggestionsCommentIds: CodeSuggestion[] = [];
-
-    //     pr.files?.forEach((file) => {
-    //         if (file.suggestions.length > 0) {
-    //             file.suggestions
-    //                 ?.filter((suggestion) =>
-    //                     suggestion.comment &&
-    //                     suggestion.deliveryStatus === DeliveryStatus.SENT &&
-    //                     suggestion.severity === SeverityLevel.CRITICAL
-    //                 )
-    //                 .forEach((filteredSuggestion) => {
-    //                     implementedSuggestionsCommentIds.push(filteredSuggestion);
-    //                 });
-    //         }
-    //     });
-
-    //     return implementedSuggestionsCommentIds;
-    // }
-
-
-
-
 }
diff --git a/src/core/infrastructure/adapters/services/github/github.service.ts b/src/core/infrastructure/adapters/services/github/github.service.ts
index b856f7d65..ff08a7d41 100644
--- a/src/core/infrastructure/adapters/services/github/github.service.ts
+++ b/src/core/infrastructure/adapters/services/github/github.service.ts
@@ -84,7 +84,10 @@ import {
     IRepositoryManager,
     REPOSITORY_MANAGER_TOKEN,
 } from '@/core/domain/repository/contracts/repository-manager.contract';
-import { CommentResult, Repository } from '@/config/types/general/codeReview.type';
+import {
+    CommentResult,
+    Repository,
+} from '@/config/types/general/codeReview.type';
 import { CreateAuthIntegrationStatus } from '@/shared/domain/enums/create-auth-integration-status.enum';
 import { ReviewComment } from '@/config/types/general/codeReview.type';
 import { getSeverityLevelShield } from '@/shared/utils/codeManagement/severityLevel';
@@ -101,7 +104,16 @@ interface GitHubAuthResponse {
 @Injectable()
 @IntegrationServiceDecorator(PlatformType.GITHUB, 'codeManagement')
 export class GithubService
-    implements IGithubService, Omit<ICodeManagementService, 'getOrganizations'> {
+    implements
+        IGithubService,
+        Omit<
+            ICodeManagementService,
+            | 'getOrganizations'
+            | 'getUserById'
+            | 'getLanguageRepository'
+            | 'createSingleIssueComment'
+        >
+{
     private readonly MAX_RETRY_ATTEMPTS = 2;
     private readonly TTL = 50 * 60 * 1000; // 50 minutes
 
@@ -134,21 +146,7 @@ export class GithubService
 
         private readonly promptService: PromptService,
         private readonly logger: PinoLoggerService,
-    ) { }
-
-
-    getUserById(params: {
-        organizationAndTeamData: OrganizationAndTeamData;
-        userId: string;
-    }): Promise<any | null> {
-        throw new Error('Method not implemented.');
-    }
-    getLanguageRepository(params: any): Promise<any | null> {
-        throw new Error('Method not implemented.');
-    }
-    createSingleIssueComment(params: any): Promise<any | null> {
-        throw new Error('Method not implemented.');
-    }
+    ) {}
 
     private async handleIntegration(
         integration: any,
@@ -1276,7 +1274,7 @@ export class GithubService
 
     private async instanceGraphQL(
         organizationAndTeamData: OrganizationAndTeamData,
-        authDetails?: GithubAuthDetail
+        authDetails?: GithubAuthDetail,
     ): Promise<typeof graphql> {
         try {
             let githubAuthDetail: GithubAuthDetail = authDetails;
@@ -1904,7 +1902,7 @@ export class GithubService
                         if (
                             secondToLastDeploy &&
                             lastDeploy.teamConfig?.configValue?.type ===
-                            'deployment'
+                                'deployment'
                         ) {
                             commits = await this.getCommitsForTagName(
                                 octokit,
@@ -1915,7 +1913,7 @@ export class GithubService
                         } else if (
                             secondToLastDeploy &&
                             lastDeploy.teamConfig?.configValue?.type ===
-                            'releases'
+                                'releases'
                         ) {
                             commits = await this.getCommitsForTagName(
                                 octokit,
@@ -2422,7 +2420,7 @@ export class GithubService
             actionStatement,
             this.formatSub(translations.talkToKody),
             this.formatSub(translations.feedback) +
-            '<!-- kody-codereview -->&#8203;\n&#8203;',
+                '<!-- kody-codereview -->&#8203;\n&#8203;',
         ]
             .join('\n')
             .trim();
@@ -2522,15 +2520,11 @@ export class GithubService
     }
 
     async getPullRequestReviewComments(params: {
-        organizationAndTeamData: OrganizationAndTeamData,
-        repository: Partial<Repository>,
-        prNumber: number,
+        organizationAndTeamData: OrganizationAndTeamData;
+        repository: Partial<Repository>;
+        prNumber: number;
     }): Promise<PullRequestReviewComment[] | null> {
-        const {
-            organizationAndTeamData,
-            repository,
-            prNumber,
-        } = params;
+        const { organizationAndTeamData, repository, prNumber } = params;
 
         const githubAuthDetail = await this.getGithubAuthDetails(
             organizationAndTeamData,
@@ -2544,7 +2538,7 @@ export class GithubService
                 repo: repository.name,
                 pull_number: prNumber,
                 per_page: 100,
-                page: 1
+                page: 1,
             });
 
             return reviewComments.data.map((comment) => ({
@@ -2556,7 +2550,7 @@ export class GithubService
                     id: comment.user.id,
                     name: comment.user?.name,
                     username: comment.user?.login,
-                }
+                },
             }));
         } catch (error) {
             this.logger.error({
@@ -2570,19 +2564,14 @@ export class GithubService
 
             return null;
         }
-
     }
 
     async getPullRequestReviewThreads(params: {
-        organizationAndTeamData: OrganizationAndTeamData,
-        repository: Partial<Repository>,
-        prNumber: number,
+        organizationAndTeamData: OrganizationAndTeamData;
+        repository: Partial<Repository>;
+        prNumber: number;
     }): Promise<PullRequestReviewComment[] | null> {
-        const {
-            organizationAndTeamData,
-            repository,
-            prNumber,
-        } = params;
+        const { organizationAndTeamData, repository, prNumber } = params;
 
         const githubAuthDetail = await this.getGithubAuthDetails(
             organizationAndTeamData,
@@ -2631,28 +2620,36 @@ export class GithubService
 
             while (hasNextPage) {
                 const response: any = await graphql(query, variables);
-                const reviewThreads = response.repository.pullRequest.reviewThreads.nodes;
-
-                const reviewComments: PullRequestReviewComment[] = reviewThreads.map((reviewThread) => {
-                    const firstComment = reviewThread.comments.nodes[0];
-
-                    // The same resource in graphQL API and REST API have different ids.
-                    // So we need one of them to actually mark the thread as resolved and the other to match the id we saved in the database.
-                    return firstComment ? {
-                        id: firstComment.id, // Used to actually resolve the thread
-                        threadId: reviewThread.id,
-                        isResolved: reviewThread.isResolved,
-                        isOutdated: reviewThread.isOutdated,
-                        fullDatabaseId: firstComment.fullDatabaseId, // The REST API id, used to match comments saved in the database.
-                        body: firstComment.body,
-                    } : null;
-                }).filter(comment => comment !== null);
+                const reviewThreads =
+                    response.repository.pullRequest.reviewThreads.nodes;
+
+                const reviewComments: PullRequestReviewComment[] = reviewThreads
+                    .map((reviewThread) => {
+                        const firstComment = reviewThread.comments.nodes[0];
+
+                        // The same resource in graphQL API and REST API have different ids.
+                        // So we need one of them to actually mark the thread as resolved and the other to match the id we saved in the database.
+                        return firstComment
+                            ? {
+                                  id: firstComment.id, // Used to actually resolve the thread
+                                  threadId: reviewThread.id,
+                                  isResolved: reviewThread.isResolved,
+                                  isOutdated: reviewThread.isOutdated,
+                                  fullDatabaseId: firstComment.fullDatabaseId, // The REST API id, used to match comments saved in the database.
+                                  body: firstComment.body,
+                              }
+                            : null;
+                    })
+                    .filter((comment) => comment !== null);
 
                 allReviewComments.push(...reviewComments);
 
                 // Check if there are more pages
-                hasNextPage = response.repository.pullRequest.reviewThreads.pageInfo.hasNextPage;
-                variables.cursor = response.repository.pullRequest.reviewThreads.pageInfo.endCursor; // Update cursor for next request
+                hasNextPage =
+                    response.repository.pullRequest.reviewThreads.pageInfo
+                        .hasNextPage;
+                variables.cursor =
+                    response.repository.pullRequest.reviewThreads.pageInfo.endCursor; // Update cursor for next request
             }
 
             return allReviewComments;
@@ -2670,15 +2667,11 @@ export class GithubService
         }
     }
 
-
     async getPullRequestsWithChangesRequested(params: {
-        organizationAndTeamData: OrganizationAndTeamData,
-        repository: Partial<Repository>,
+        organizationAndTeamData: OrganizationAndTeamData;
+        repository: Partial<Repository>;
     }): Promise<PullRequestsWithChangesRequested[] | null> {
-        const {
-            organizationAndTeamData,
-            repository,
-        } = params;
+        const { organizationAndTeamData, repository } = params;
 
         const githubAuthDetail = await this.getGithubAuthDetails(
             organizationAndTeamData,
@@ -2708,9 +2701,14 @@ export class GithubService
         try {
             const response: any = await graphql(query, variables);
 
-            const prs: PullRequestsWithChangesRequested[] = response.repository.pullRequests.nodes;
+            const prs: PullRequestsWithChangesRequested[] =
+                response.repository.pullRequests.nodes;
 
-            const prsWithRequestedChanges = prs.filter((pr) => pr.reviewDecision === PullRequestReviewState.CHANGES_REQUESTED);
+            const prsWithRequestedChanges = prs.filter(
+                (pr) =>
+                    pr.reviewDecision ===
+                    PullRequestReviewState.CHANGES_REQUESTED,
+            );
 
             return prsWithRequestedChanges;
         } catch (error) {
@@ -2727,7 +2725,6 @@ export class GithubService
         }
     }
 
-
     private sanitizeLine(line: string | number): number {
         return typeof line === 'string' ? parseInt(line, 10) : line;
     }
@@ -3006,12 +3003,8 @@ export class GithubService
             });
             throw new BadRequestException('Failed to resolve review thread.');
         }
-
     }
 
-
-
-
     async findTeamAndOrganizationIdByConfigKey(
         params: any,
     ): Promise<IntegrationConfigEntity | null> {
@@ -3220,15 +3213,15 @@ export class GithubService
                 reactions: {
                     thumbsUp: isOAuth
                         ? Math.max(
-                            0,
-                            comment.reactions[GitHubReaction.THUMBS_UP] - 1,
-                        )
+                              0,
+                              comment.reactions[GitHubReaction.THUMBS_UP] - 1,
+                          )
                         : comment.reactions[GitHubReaction.THUMBS_UP],
                     thumbsDown: isOAuth
                         ? Math.max(
-                            0,
-                            comment.reactions[GitHubReaction.THUMBS_DOWN] - 1,
-                        )
+                              0,
+                              comment.reactions[GitHubReaction.THUMBS_DOWN] - 1,
+                          )
                         : comment.reactions[GitHubReaction.THUMBS_DOWN],
                 },
                 comment: {
@@ -3545,11 +3538,16 @@ export class GithubService
     async requestChangesPullRequest(params: {
         organizationAndTeamData: OrganizationAndTeamData;
         prNumber: number;
-        repository: { id: string; name: string },
-        criticalComments: CommentResult[]
+        repository: { id: string; name: string };
+        criticalComments: CommentResult[];
     }) {
         try {
-            const { organizationAndTeamData, prNumber, repository, criticalComments } = params;
+            const {
+                organizationAndTeamData,
+                prNumber,
+                repository,
+                criticalComments,
+            } = params;
 
             const githubAuthDetail = await this.getGithubAuthDetails(
                 organizationAndTeamData,
@@ -3561,12 +3559,14 @@ export class GithubService
                 criticalComments,
                 orgName: githubAuthDetail.org,
                 repository,
-                prNumber
+                prNumber,
             });
 
-            const requestChangeBodyTitle = '# Found critical issues please review the requested changes';
+            const requestChangeBodyTitle =
+                '# Found critical issues please review the requested changes';
 
-            const formattedBody = `${requestChangeBodyTitle}\n\n${listOfCriticalIssues}`.trim();
+            const formattedBody =
+                `${requestChangeBodyTitle}\n\n${listOfCriticalIssues}`.trim();
 
             await octokit.rest.pulls.createReview({
                 owner: githubAuthDetail.org,
@@ -3595,40 +3595,47 @@ export class GithubService
     }
 
     getListOfCriticalIssues(params: {
-        criticalComments: CommentResult[],
-        orgName: string,
-        repository: Partial<IRepository>,
+        criticalComments: CommentResult[];
+        orgName: string;
+        repository: Partial<IRepository>;
         prNumber: number;
     }): string {
         const { criticalComments, orgName, prNumber, repository } = params;
 
-        const criticalIssuesSummaryArray = this.getCriticalIssuesSummaryArray(criticalComments);
+        const criticalIssuesSummaryArray =
+            this.getCriticalIssuesSummaryArray(criticalComments);
 
-        const listOfCriticalIssues = criticalIssuesSummaryArray.map((criticalIssue) => {
-            const commentId = criticalIssue.id;
-            const summary = criticalIssue.oneSentenceSummary;
+        const listOfCriticalIssues = criticalIssuesSummaryArray
+            .map((criticalIssue) => {
+                const commentId = criticalIssue.id;
+                const summary = criticalIssue.oneSentenceSummary;
 
-            const link = !orgName || !repository?.name || !prNumber || !commentId
-                ? ''
-                : `https://github.com/${orgName}/${repository.name}/pull/${prNumber}#discussion_r${commentId}`;
+                const link =
+                    !orgName || !repository?.name || !prNumber || !commentId
+                        ? ''
+                        : `https://github.com/${orgName}/${repository.name}/pull/${prNumber}#discussion_r${commentId}`;
 
-            const formattedItem = commentId
-                ? (`- [${summary}](${link})`)
-                : (`- ${summary}`)
+                const formattedItem = commentId
+                    ? `- [${summary}](${link})`
+                    : `- ${summary}`;
 
-            return formattedItem.trim();
-        }).join("\n")
+                return formattedItem.trim();
+            })
+            .join('\n');
 
         return listOfCriticalIssues;
     }
 
-    getCriticalIssuesSummaryArray(criticalComments: CommentResult[]): OneSentenceSummaryItem[] {
+    getCriticalIssuesSummaryArray(
+        criticalComments: CommentResult[],
+    ): OneSentenceSummaryItem[] {
         const criticalIssuesSummaryArray: OneSentenceSummaryItem[] =
             criticalComments.map((comment) => {
                 return {
                     id: comment.codeReviewFeedbackData.commentId,
-                    oneSentenceSummary: comment.comment.suggestion.oneSentenceSummary ?? "",
-                }
+                    oneSentenceSummary:
+                        comment.comment.suggestion.oneSentenceSummary ?? '',
+                };
             });
 
         return criticalIssuesSummaryArray;
@@ -3787,11 +3794,7 @@ export class GithubService
         repository: Partial<Repository>;
         prNumber: number;
     }): Promise<any[] | null> {
-        const {
-            organizationAndTeamData,
-            repository,
-            prNumber,
-        } = params;
+        const { organizationAndTeamData, repository, prNumber } = params;
 
         const githubAuthDetail = await this.getGithubAuthDetails(
             organizationAndTeamData,
@@ -3847,72 +3850,86 @@ export class GithubService
             const response: any = await graphql(query, variables);
 
             const reviews = response.repository.pullRequest.reviews.nodes;
-            const reviewThreads = response.repository.pullRequest.reviewThreads.nodes;
-
-            const reviewThreadComments: PullRequestReviewComment[] = reviewThreads.map((reviewThread) => {
-                const firstComment = reviewThread.comments.nodes[0];
-
-                // The same resource in graphQL API and REST API have different ids.
-                // So we need one of them to actually mark the thread as resolved and the other to match the id we saved in the database.
-                return firstComment ? {
-                    id: firstComment.id, // Used to actually resolve the thread
-                    threadId: reviewThread.id,
-                    isResolved: reviewThread.isResolved,
-                    isOutdated: reviewThread.isOutdated,
-                    fullDatabaseId: firstComment.fullDatabaseId, // The REST API id, used to match comments saved in the database.
-                    body: firstComment.body,
-                } : null;
-            }).filter(comment => comment !== null);
-
-            const reviewsThatRequestedChanges = reviews
-                .filter((review) => review.state === PullRequestReviewState.CHANGES_REQUESTED)
+            const reviewThreads =
+                response.repository.pullRequest.reviewThreads.nodes;
+
+            const reviewThreadComments: PullRequestReviewComment[] =
+                reviewThreads
+                    .map((reviewThread) => {
+                        const firstComment = reviewThread.comments.nodes[0];
+
+                        // The same resource in graphQL API and REST API have different ids.
+                        // So we need one of them to actually mark the thread as resolved and the other to match the id we saved in the database.
+                        return firstComment
+                            ? {
+                                  id: firstComment.id, // Used to actually resolve the thread
+                                  threadId: reviewThread.id,
+                                  isResolved: reviewThread.isResolved,
+                                  isOutdated: reviewThread.isOutdated,
+                                  fullDatabaseId: firstComment.fullDatabaseId, // The REST API id, used to match comments saved in the database.
+                                  body: firstComment.body,
+                              }
+                            : null;
+                    })
+                    .filter((comment) => comment !== null);
+
+            const reviewsThatRequestedChanges = reviews.filter(
+                (review) =>
+                    review.state === PullRequestReviewState.CHANGES_REQUESTED,
+            );
 
             if (reviewsThatRequestedChanges.length < 1) {
                 return [];
             }
 
-            const reviewsComments: any[] = reviewsThatRequestedChanges.map((review) => {
-                const firstComment = review?.comments?.nodes[0];
+            const reviewsComments: any[] = reviewsThatRequestedChanges
+                .map((review) => {
+                    const firstComment = review?.comments?.nodes[0];
 
-                if (!firstComment) {
-                    return {
-                        reviewId: review.id,
+                    if (!firstComment) {
+                        return {
+                            reviewId: review.id,
+                        };
                     }
-                }
-                // The same resource in graphQL API and REST API have different ids.
-                // So we need one of them to actually mark the thread as resolved and the other to match the id we saved in the database.
-                return firstComment ? {
-                    id: firstComment.id, // Used to actually resolve the thread
-                    reviewId: review.id,
-                    fullDatabaseId: firstComment.fullDatabaseId, // The REST API id, used to match comments saved in the database.
-                    body: firstComment.body,
-                } : null;
-            }).filter(comment => comment !== null);
+                    // The same resource in graphQL API and REST API have different ids.
+                    // So we need one of them to actually mark the thread as resolved and the other to match the id we saved in the database.
+                    return firstComment
+                        ? {
+                              id: firstComment.id, // Used to actually resolve the thread
+                              reviewId: review.id,
+                              fullDatabaseId: firstComment.fullDatabaseId, // The REST API id, used to match comments saved in the database.
+                              body: firstComment.body,
+                          }
+                        : null;
+                })
+                .filter((comment) => comment !== null);
 
             const validReviews = reviewsComments
-                .map(reviewComment => {
-                    const matchingThreadComment = reviewThreadComments.find(threadComment => threadComment.id === reviewComment.id);
+                .map((reviewComment) => {
+                    const matchingThreadComment = reviewThreadComments.find(
+                        (threadComment) =>
+                            threadComment.id === reviewComment.id,
+                    );
 
                     if (matchingThreadComment) {
                         return {
                             ...reviewComment,
                             isResolved: matchingThreadComment?.isResolved,
-                            isOutdated: matchingThreadComment?.isOutdated
+                            isOutdated: matchingThreadComment?.isOutdated,
                         };
                     }
 
                     return null;
                 })
-                .filter(comment => comment !== null);
+                .filter((comment) => comment !== null);
             return validReviews;
-
         } catch (error) {
             this.logger.error({
                 message: `Error retrieving list of valid reviews for PR#${prNumber}`,
                 context: GithubService.name,
                 error: error,
                 metadata: {
-                    ...params
+                    ...params,
                 },
             });
 
diff --git a/src/core/infrastructure/adapters/services/gitlab.service.ts b/src/core/infrastructure/adapters/services/gitlab.service.ts
index f0ca3a3ab..712ba0aae 100644
--- a/src/core/infrastructure/adapters/services/gitlab.service.ts
+++ b/src/core/infrastructure/adapters/services/gitlab.service.ts
@@ -83,6 +83,9 @@ export class GitlabService
             | 'getRepositoryAllFiles'
             | 'getAuthenticationOAuthToken'
             | 'getPullRequestDetails'
+            | 'getCommitsByReleaseMode'
+            | 'getDataForCalculateDeployFrequency'
+            | 'requestChangesPullRequest'
         >
 {
     constructor(
@@ -1255,16 +1258,6 @@ export class GitlabService
         }
     }
 
-    getCommitsByReleaseMode(params: any): Promise<CommitLeadTimeForChange[]> {
-        throw new Error('Method not implemented.');
-    }
-
-    getDataForCalculateDeployFrequency(
-        params: any,
-    ): Promise<DeployFrequency[]> {
-        throw new Error('Method not implemented.');
-    }
-
     private async getPullRequestFiles(
         gitlab: InstanceType<typeof Gitlab>,
         projectId: string,
@@ -2268,16 +2261,6 @@ export class GitlabService
         }
     }
 
-    async requestChangesPullRequest(params: any) {
-        this.logger.error({
-            message: `Request changes pull request not implemented`,
-            context: GitlabService.name,
-            serviceName: 'GitlabService requestChangesPullRequest',
-            metadata: params,
-        });
-        throw new Error('Method not implemented.');
-    }
-
     async getAllCommentsInPullRequest(params: {
         organizationAndTeamData: OrganizationAndTeamData;
         repository: { name: string; id: string };
diff --git a/src/core/infrastructure/adapters/services/pullRequests/pullRequests.service.ts b/src/core/infrastructure/adapters/services/pullRequests/pullRequests.service.ts
index eee1b5c0c..11f9fc746 100644
--- a/src/core/infrastructure/adapters/services/pullRequests/pullRequests.service.ts
+++ b/src/core/infrastructure/adapters/services/pullRequests/pullRequests.service.ts
@@ -390,7 +390,8 @@ export class PullRequestsService implements IPullRequestsService {
             pullRequest.state === 'DECLINED' ||
             pullRequest.state === 'merge' ||
             pullRequest.state === 'merged' ||
-            pullRequest.state === 'MERGED'
+            pullRequest.state === 'MERGED' ||
+            pullRequest.status === 'completed'
         ) {
             return PullRequestState.CLOSED;
         } else {
@@ -864,7 +865,7 @@ export class PullRequestsService implements IPullRequestsService {
             return pullRequest?.updated_at || pullRequest?.updated_on || '';
         }
 
-        return pullRequest.closed_at || '';
+        return pullRequest.closed_at || pullRequest.closedDate || '';
     }
 
     private extractRepoFullName(pullRequest: any): string {
diff --git a/yarn.lock b/yarn.lock
index 9cb47104f..6055a242f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1223,18 +1223,11 @@
     debug "^4.3.1"
     minimatch "^3.1.2"
 
-"@eslint/config-helpers@^0.2.0":
+"@eslint/config-helpers@^0.2.1":
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.2.1.tgz#26042c028d1beee5ce2235a7929b91c52651646d"
   integrity sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==
 
-"@eslint/core@^0.12.0":
-  version "0.12.0"
-  resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.12.0.tgz#5f960c3d57728be9f6c65bd84aa6aa613078798e"
-  integrity sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==
-  dependencies:
-    "@types/json-schema" "^7.0.15"
-
 "@eslint/core@^0.13.0":
   version "0.13.0"
   resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.13.0.tgz#bf02f209846d3bf996f9e8009db62df2739b458c"
@@ -1257,17 +1250,17 @@
     minimatch "^3.1.2"
     strip-json-comments "^3.1.1"
 
-"@eslint/js@9.24.0":
-  version "9.24.0"
-  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.24.0.tgz#685277980bb7bf84ecc8e4e133ccdda7545a691e"
-  integrity sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==
+"@eslint/js@9.25.1":
+  version "9.25.1"
+  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.25.1.tgz#25f5c930c2b68b5ebe7ac857f754cbd61ef6d117"
+  integrity sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==
 
 "@eslint/object-schema@^2.1.6":
   version "2.1.6"
   resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f"
   integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==
 
-"@eslint/plugin-kit@^0.2.7":
+"@eslint/plugin-kit@^0.2.8":
   version "0.2.8"
   resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz#47488d8f8171b5d4613e833313f3ce708e3525f8"
   integrity sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==
@@ -1285,32 +1278,32 @@
   resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.7.0.tgz#1cf1fecfcad5e2da2332140bf3b5f23cc1c2a7f4"
   integrity sha512-aozo5vqjCmDoXLNUJarFZx2IN/GgGaogY4TMJ6so/WLZOWpSV7fvj2dmrV6sEAnUm1O7aCrhTibjpzeDFgNqbg==
 
-"@gitbeaker/core@^42.2.0":
-  version "42.2.0"
-  resolved "https://registry.yarnpkg.com/@gitbeaker/core/-/core-42.2.0.tgz#a6daf76fa8bfe66d1fe03d94b58c6c00f93efea8"
-  integrity sha512-pUFESP5I0/f2bqSNXuI+pM+ZdpipFexIscywTw2M6+N1L4z5hH+ww9KHVl7EdE0r046ZFsVLu4MukQipWCKfLg==
+"@gitbeaker/core@^42.4.0":
+  version "42.4.0"
+  resolved "https://registry.yarnpkg.com/@gitbeaker/core/-/core-42.4.0.tgz#09adb00f5b1755faba6be80da7989432781c90c1"
+  integrity sha512-XYldXmVlIXR9MgyjuIRsoYcQ36nDIWedRy0vvcoWdj4w/B+qZZx4m4kQij2CqOsvK6REIiUIrsnsoWJpHUY0ZA==
   dependencies:
-    "@gitbeaker/requester-utils" "^42.2.0"
+    "@gitbeaker/requester-utils" "^42.4.0"
     qs "^6.12.2"
     xcase "^2.0.1"
 
-"@gitbeaker/requester-utils@^42.2.0":
-  version "42.2.0"
-  resolved "https://registry.yarnpkg.com/@gitbeaker/requester-utils/-/requester-utils-42.2.0.tgz#76610234bbda4e91618b49e832641a1f78af4a4f"
-  integrity sha512-8p9N2apidpUNwodRWT5/WjfIS9pKJBcoRAud4+qvvRNX99SO3sn9YVDEfA3eIjBHsc7E9soVhGQvvRV2HSP+JA==
+"@gitbeaker/requester-utils@^42.4.0":
+  version "42.4.0"
+  resolved "https://registry.yarnpkg.com/@gitbeaker/requester-utils/-/requester-utils-42.4.0.tgz#9e4729ca54546c3042b01f7a678320efafa212d7"
+  integrity sha512-QFEs3tprFqTRgm9/9M0lV+ly+LaOGp7KZV+deWbbbmbjeTEiVrIMGH7EM8mDf+sfYKJGEbbE+T7x4JDg2bPJEA==
   dependencies:
     picomatch-browser "^2.2.6"
     qs "^6.12.2"
     rate-limiter-flexible "^4.0.1"
     xcase "^2.0.1"
 
-"@gitbeaker/rest@^42.2.0":
-  version "42.2.0"
-  resolved "https://registry.yarnpkg.com/@gitbeaker/rest/-/rest-42.2.0.tgz#ac7e6c0b0b0a6503a94ed4dc6e20e9c700817745"
-  integrity sha512-d7LDVuu85/HNVSQajajfTpgDJNaX+/KICyV+Q8mKW8tVTK4K61OTfS05Q0XI4JFHxHNAB1sF+OnvR3C6/jQAJA==
+"@gitbeaker/rest@^42.4.0":
+  version "42.4.0"
+  resolved "https://registry.yarnpkg.com/@gitbeaker/rest/-/rest-42.4.0.tgz#06a49d5e344e2dde29b359c845e2b33a3cd098ea"
+  integrity sha512-6QQxQc3OhIyH7Pma69K5vIk8yG4VwaHmItpPG0cUc6Z66jOfaOzYpbrdZI1fLZvc6uowb3CwTYdMRie/x2Ld2w==
   dependencies:
-    "@gitbeaker/core" "^42.2.0"
-    "@gitbeaker/requester-utils" "^42.2.0"
+    "@gitbeaker/core" "^42.4.0"
+    "@gitbeaker/requester-utils" "^42.4.0"
 
 "@golevelup/nestjs-discovery@^4.0.3":
   version "4.0.3"
@@ -1850,10 +1843,10 @@
     zod "^3.22.3"
     zod-to-json-schema "^3.22.5"
 
-"@langchain/core@^0.3.45":
-  version "0.3.45"
-  resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.3.45.tgz#149d8fe6f44d0fec78573791dcb4390bbfb10324"
-  integrity sha512-4icCMCeuauVvwH43zgCE6Hk2Zbke5OccfVhFpTiIJPo8WoU/ALdaZlS22S+6+qZ9lFzFHsgZ6K33Av9aWhxkQA==
+"@langchain/core@^0.3.46":
+  version "0.3.46"
+  resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.3.46.tgz#4bf7982fd74d48ce68c0e651436dfd5271b78a47"
+  integrity sha512-uZ9iI8OIWjOoY/OBkNrjobYHPI/Ky/X4NllKLtkAaKt0aI02HAm2EmZnGrUd0oHjwlYLzjat0FE329ACqiXadw==
   dependencies:
     "@cfworker/json-schema" "^4.0.2"
     ansi-styles "^5.0.0"
@@ -1907,7 +1900,7 @@
   dependencies:
     mongodb "^6.3.0"
 
-"@langchain/openai@>=0.1.0 <0.6.0", "@langchain/openai@>=0.2.0 <0.6.0", "@langchain/openai@^0.5.5":
+"@langchain/openai@>=0.1.0 <0.6.0", "@langchain/openai@>=0.2.0 <0.6.0":
   version "0.5.5"
   resolved "https://registry.yarnpkg.com/@langchain/openai/-/openai-0.5.5.tgz#8c6ac88e7269acb317ffd39f4d57012008d2d023"
   integrity sha512-QwdZrWcx6FB+UMKQ6+a0M9ZXzeUnZCwXP7ltqCCycPzdfiwxg3TQ6WkSefdEyiPpJcVVq/9HZSxrzGmf18QGyw==
@@ -1917,6 +1910,16 @@
     zod "^3.22.4"
     zod-to-json-schema "^3.22.3"
 
+"@langchain/openai@^0.5.6":
+  version "0.5.6"
+  resolved "https://registry.yarnpkg.com/@langchain/openai/-/openai-0.5.6.tgz#d58e5a4c84b3c2ee29434468091cde693c6d3cc0"
+  integrity sha512-zN0iyJthPNmcefIBVybZwcTBgcqu/ElJFov42ZntxEncK4heOMAE9lkq9LQ5CaPU/SgrduibrM1oL57+tLUtaA==
+  dependencies:
+    js-tiktoken "^1.0.12"
+    openai "^4.93.0"
+    zod "^3.22.4"
+    zod-to-json-schema "^3.22.3"
+
 "@langchain/textsplitters@>=0.0.0 <0.2.0":
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/@langchain/textsplitters/-/textsplitters-0.1.0.tgz#f37620992192df09ecda3dfbd545b36a6bcbae46"
@@ -4134,62 +4137,62 @@
   dependencies:
     "@types/yargs-parser" "*"
 
-"@typescript-eslint/eslint-plugin@^8.30.1":
-  version "8.30.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz#9beb9e4fbfdde40410e96587cc56dded1942cdf1"
-  integrity sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==
+"@typescript-eslint/eslint-plugin@^8.31.0":
+  version "8.31.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.0.tgz#ef3ece95406a80026f82a19a2984c1e375981711"
+  integrity sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==
   dependencies:
     "@eslint-community/regexpp" "^4.10.0"
-    "@typescript-eslint/scope-manager" "8.30.1"
-    "@typescript-eslint/type-utils" "8.30.1"
-    "@typescript-eslint/utils" "8.30.1"
-    "@typescript-eslint/visitor-keys" "8.30.1"
+    "@typescript-eslint/scope-manager" "8.31.0"
+    "@typescript-eslint/type-utils" "8.31.0"
+    "@typescript-eslint/utils" "8.31.0"
+    "@typescript-eslint/visitor-keys" "8.31.0"
     graphemer "^1.4.0"
     ignore "^5.3.1"
     natural-compare "^1.4.0"
     ts-api-utils "^2.0.1"
 
-"@typescript-eslint/parser@^8.30.1":
-  version "8.30.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.30.1.tgz#8a9fa650b046e64656e21d4fdff86535b6a084b6"
-  integrity sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==
+"@typescript-eslint/parser@^8.31.0":
+  version "8.31.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.31.0.tgz#5ec28823d06dd20ed5f67b61224823f12ccde095"
+  integrity sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==
   dependencies:
-    "@typescript-eslint/scope-manager" "8.30.1"
-    "@typescript-eslint/types" "8.30.1"
-    "@typescript-eslint/typescript-estree" "8.30.1"
-    "@typescript-eslint/visitor-keys" "8.30.1"
+    "@typescript-eslint/scope-manager" "8.31.0"
+    "@typescript-eslint/types" "8.31.0"
+    "@typescript-eslint/typescript-estree" "8.31.0"
+    "@typescript-eslint/visitor-keys" "8.31.0"
     debug "^4.3.4"
 
-"@typescript-eslint/scope-manager@8.30.1":
-  version "8.30.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz#f99c7efd53b5ff9fb57e55be71eb855603fd80b7"
-  integrity sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==
+"@typescript-eslint/scope-manager@8.31.0":
+  version "8.31.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.31.0.tgz#48c7f7d729ea038e36cae0ff511e48c2412fb11c"
+  integrity sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==
   dependencies:
-    "@typescript-eslint/types" "8.30.1"
-    "@typescript-eslint/visitor-keys" "8.30.1"
+    "@typescript-eslint/types" "8.31.0"
+    "@typescript-eslint/visitor-keys" "8.31.0"
 
-"@typescript-eslint/type-utils@8.30.1":
-  version "8.30.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz#151ee0529d6e6df19d8a3a23e81c809d2e4f6b1a"
-  integrity sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==
+"@typescript-eslint/type-utils@8.31.0":
+  version "8.31.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.31.0.tgz#01536a993fae23e2def885b006aaa991cbfbe9e7"
+  integrity sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==
   dependencies:
-    "@typescript-eslint/typescript-estree" "8.30.1"
-    "@typescript-eslint/utils" "8.30.1"
+    "@typescript-eslint/typescript-estree" "8.31.0"
+    "@typescript-eslint/utils" "8.31.0"
     debug "^4.3.4"
     ts-api-utils "^2.0.1"
 
-"@typescript-eslint/types@8.30.1":
-  version "8.30.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.30.1.tgz#20ff6d66ab3d8fe0533aeb7092a487393d53f925"
-  integrity sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==
+"@typescript-eslint/types@8.31.0":
+  version "8.31.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.31.0.tgz#c48e20ec47a43b72747714f49ea9f7b38a4fa6c1"
+  integrity sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==
 
-"@typescript-eslint/typescript-estree@8.30.1":
-  version "8.30.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz#f5c133e4a76a54d25607434f2c276409d7bec4ba"
-  integrity sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==
+"@typescript-eslint/typescript-estree@8.31.0":
+  version "8.31.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.0.tgz#9c7f84eff6ad23d63cf086c6e93af571cd561270"
+  integrity sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==
   dependencies:
-    "@typescript-eslint/types" "8.30.1"
-    "@typescript-eslint/visitor-keys" "8.30.1"
+    "@typescript-eslint/types" "8.31.0"
+    "@typescript-eslint/visitor-keys" "8.31.0"
     debug "^4.3.4"
     fast-glob "^3.3.2"
     is-glob "^4.0.3"
@@ -4197,22 +4200,22 @@
     semver "^7.6.0"
     ts-api-utils "^2.0.1"
 
-"@typescript-eslint/utils@8.30.1":
-  version "8.30.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.30.1.tgz#23d4824394765948fe73dc7113892f85fdc80efd"
-  integrity sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==
+"@typescript-eslint/utils@8.31.0":
+  version "8.31.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.31.0.tgz#6fb52471a29fdd16fc253d568c5ad4b048f78ba4"
+  integrity sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==
   dependencies:
     "@eslint-community/eslint-utils" "^4.4.0"
-    "@typescript-eslint/scope-manager" "8.30.1"
-    "@typescript-eslint/types" "8.30.1"
-    "@typescript-eslint/typescript-estree" "8.30.1"
+    "@typescript-eslint/scope-manager" "8.31.0"
+    "@typescript-eslint/types" "8.31.0"
+    "@typescript-eslint/typescript-estree" "8.31.0"
 
-"@typescript-eslint/visitor-keys@8.30.1":
-  version "8.30.1"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz#510955ef1fb56e08da4b7953a3377258e5942e36"
-  integrity sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==
+"@typescript-eslint/visitor-keys@8.31.0":
+  version "8.31.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.0.tgz#9a1a97ed16c60d4d1e7399b41c11a6d94ebc1ce5"
+  integrity sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==
   dependencies:
-    "@typescript-eslint/types" "8.30.1"
+    "@typescript-eslint/types" "8.31.0"
     eslint-visitor-keys "^4.2.0"
 
 "@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1":
@@ -5656,19 +5659,19 @@ eslint-visitor-keys@^4.2.0:
   resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45"
   integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==
 
-eslint@^9.24.0:
-  version "9.24.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.24.0.tgz#9a7f2e6cb2de81c405ab244b02f4584c79dc6bee"
-  integrity sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==
+eslint@^9.25.1:
+  version "9.25.1"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.25.1.tgz#8a7cf8dd0e6acb858f86029720adb1785ee57580"
+  integrity sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==
   dependencies:
     "@eslint-community/eslint-utils" "^4.2.0"
     "@eslint-community/regexpp" "^4.12.1"
     "@eslint/config-array" "^0.20.0"
-    "@eslint/config-helpers" "^0.2.0"
-    "@eslint/core" "^0.12.0"
+    "@eslint/config-helpers" "^0.2.1"
+    "@eslint/core" "^0.13.0"
     "@eslint/eslintrc" "^3.3.1"
-    "@eslint/js" "9.24.0"
-    "@eslint/plugin-kit" "^0.2.7"
+    "@eslint/js" "9.25.1"
+    "@eslint/plugin-kit" "^0.2.8"
     "@humanfs/node" "^0.16.6"
     "@humanwhocodes/module-importer" "^1.0.1"
     "@humanwhocodes/retry" "^0.4.2"
@@ -7319,10 +7322,10 @@ kleur@^3.0.3:
     zod "^3.22.4"
     zod-to-json-schema "^3.22.3"
 
-langchain@^0.3.22:
-  version "0.3.22"
-  resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.3.22.tgz#be96d5809fa79b2b045d0c0b8737274baa46a8c1"
-  integrity sha512-FVH7XKBKIEHX6DJmFIPE9yUG0vBUkrgKebQfW9BfDmnhfHOiVLns/POmSGj+/GkyJOb5I3mD6iRKYLdTKWNTYQ==
+langchain@^0.3.23:
+  version "0.3.23"
+  resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.3.23.tgz#1bc8c19630fdbe85fffb50c741994e7b6707cf0d"
+  integrity sha512-o297nfr78Dm1LxkivJ3ULZoHHC/e5e9ruX1kyIIKUlw1O3lyb8nXCnGKWh96zJRD+BpN0akRRKiJruRYYWTkdw==
   dependencies:
     "@langchain/openai" ">=0.1.0 <0.6.0"
     "@langchain/textsplitters" ">=0.0.0 <0.2.0"
@@ -7337,10 +7340,10 @@ langchain@^0.3.22:
     zod "^3.22.4"
     zod-to-json-schema "^3.22.3"
 
-langsmith@0.3.18:
-  version "0.3.18"
-  resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.3.18.tgz#fba373ce0a37d16e0989eb7bf71e6d1e9b44772d"
-  integrity sha512-VTD5DZI7qZsOipjp/WaLBIMsdT4Y2OA+9nxyEgPQd8srF4hRx3sIydgxARkB4N5E4yTwo1S42FpHoPNBvwwE/A==
+langsmith@0.3.20:
+  version "0.3.20"
+  resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.3.20.tgz#9a6951871cc35fbc98b19cadb4dda92e0d6e6836"
+  integrity sha512-zwVQos6tjcksCTfdM67QKq7yyED4GmQiZw/sJ6UCMYZxlvTMMg3PeQ9tOePXAWNWoJygOnH+EwGXr7gYOOETDg==
   dependencies:
     "@types/uuid" "^10.0.0"
     chalk "^4.1.2"
@@ -8074,10 +8077,10 @@ openai@^4.87.3:
     formdata-node "^4.3.2"
     node-fetch "^2.6.7"
 
-openai@^4.95.0:
-  version "4.95.0"
-  resolved "https://registry.yarnpkg.com/openai/-/openai-4.95.0.tgz#e391f02561874b7cd5e8becd56feec77d88be32f"
-  integrity sha512-tWHLTA+/HHyWlP8qg0mQLDSpI2NQLhk6zHLJL8yb59qn2pEI8rbEiAGSDPViLvi3BRDoQZIX5scaJ3xYGr2nhw==
+openai@^4.93.0, openai@^4.95.1:
+  version "4.95.1"
+  resolved "https://registry.yarnpkg.com/openai/-/openai-4.95.1.tgz#7157697c2b150a546b13eb860180c4a6058051da"
+  integrity sha512-IqJy+ymeW+k/Wq+2YVN3693OQMMcODRtHEYOlz263MdUwnN/Dwdl9c2EXSxLLtGEHkSHAfvzpDMHI5MaWJKXjQ==
   dependencies:
     "@types/node" "^18.11.18"
     "@types/node-fetch" "^2.6.4"
@@ -8310,11 +8313,21 @@ pg-cloudflare@^1.1.1:
   resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz#e6d5833015b170e23ae819e8c5d7eaedb472ca98"
   integrity sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==
 
+pg-cloudflare@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.2.0.tgz#5b3a7a5cc98e1dce4dfadd2d1b9402665b711536"
+  integrity sha512-dhfTv9ZhBLHHjFjh1qeA/0xbaF9ax9fVp+jFCWEDR3Qtdj6oM+GybEewsvAjDkH1mu6dpiH2MSKSHmVDrZqrGA==
+
 pg-connection-string@^2.7.0:
   version "2.7.0"
   resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.7.0.tgz#f1d3489e427c62ece022dba98d5262efcb168b37"
   integrity sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==
 
+pg-connection-string@^2.8.1:
+  version "2.8.1"
+  resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.8.1.tgz#9d6b878b14459ff5c6d9f311358d6c758b1ec500"
+  integrity sha512-/K3E7SgRJms0N/GCEI5IsrfY86FzTAfox/5nRfJT0I1NGM0JPutpbKfVOSRnlMkIJYFmlyQ7+5jHEfL9IIPFLQ==
+
 pg-int8@1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
@@ -8335,6 +8348,11 @@ pg-pool@^3.8.0:
   resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.8.0.tgz#e6bce7fc4506a8d6106551363fc5283e5445b776"
   integrity sha512-VBw3jiVm6ZOdLBTIcXLNdSotb6Iy3uOCwDGFAksZCXmi10nyRvnP2v3jl4d+IsLYRyXf6o9hIm/ZtUzlByNUdw==
 
+pg-pool@^3.9.1:
+  version "3.9.1"
+  resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.9.1.tgz#0cba0562edc4787848128711c69fea0344a08cd4"
+  integrity sha512-gC8F55MsCWsFmiXlNVzoyYU5tvhDrKqnpKG+YJzxjGwbOQxFHy/AS/QxuG8723OrxC6kTHxIrbAeelVAePqJyw==
+
 pg-promise@^11.13.0:
   version "11.13.0"
   resolved "https://registry.yarnpkg.com/pg-promise/-/pg-promise-11.13.0.tgz#90c2c7c5cd638a3ca6e8cde9f18d29dae333297d"
@@ -8350,6 +8368,11 @@ pg-protocol@*, pg-protocol@^1.8.0:
   resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.8.0.tgz#c707101dd07813868035a44571488e4b98639d48"
   integrity sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==
 
+pg-protocol@^1.9.0:
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.9.0.tgz#d2a1f4289df6e969ff6c29753c05a4f88ca08911"
+  integrity sha512-6AkqYKbOwHVlDnXrOZlSkpvn9zBA4y87HGlrPhnSPLkcLRnTPcXm2C/CNrOkQB2T7QKYv+IzCcmW0K974n3z9Q==
+
 pg-types@^2.1.0, pg-types@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3"
@@ -8374,7 +8397,7 @@ pg-types@^4.0.1:
     postgres-interval "^3.0.0"
     postgres-range "^1.1.1"
 
-pg@8.14.1, pg@^8.14.1:
+pg@8.14.1:
   version "8.14.1"
   resolved "https://registry.yarnpkg.com/pg/-/pg-8.14.1.tgz#2e3d1f287b64797cdfc8d1ba000f61a7ff8d66ed"
   integrity sha512-0TdbqfjwIun9Fm/r89oB7RFQ0bLgduAhiIqIXOsyKoiC/L54DbuAAzIEN/9Op0f1Po9X7iCPXGoa/Ah+2aI8Xw==
@@ -8387,6 +8410,19 @@ pg@8.14.1, pg@^8.14.1:
   optionalDependencies:
     pg-cloudflare "^1.1.1"
 
+pg@^8.15.1:
+  version "8.15.1"
+  resolved "https://registry.yarnpkg.com/pg/-/pg-8.15.1.tgz#3b17971e8319d2bf8ad2084f36bd2aacf5944977"
+  integrity sha512-tC0r/yaCpAcWdYyjxtiNw9PCxlPJg+XJfm+Gzz1wpPTql5LYUHmYqXFEkJwpLEMKqpnevR/fqvXEa1O5O0sWiw==
+  dependencies:
+    pg-connection-string "^2.8.1"
+    pg-pool "^3.9.1"
+    pg-protocol "^1.9.0"
+    pg-types "^2.1.0"
+    pgpass "1.x"
+  optionalDependencies:
+    pg-cloudflare "^1.2.0"
+
 pgpass@1.x:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.5.tgz#9b873e4a564bb10fa7a7dbd55312728d422a223d"
@@ -9387,10 +9423,10 @@ thread-stream@^3.0.0:
   dependencies:
     real-require "^0.2.0"
 
-tiktoken@^1.0.20:
-  version "1.0.20"
-  resolved "https://registry.yarnpkg.com/tiktoken/-/tiktoken-1.0.20.tgz#a6d7ce757c009e7b4bc757aee7d512187b4b92a6"
-  integrity sha512-zVIpXp84kth/Ni2me1uYlJgl2RZ2EjxwDaWLeDY/s6fZiyO9n1QoTOM5P7ZSYfToPvAvwYNMbg5LETVYVKyzfQ==
+tiktoken@^1.0.21:
+  version "1.0.21"
+  resolved "https://registry.yarnpkg.com/tiktoken/-/tiktoken-1.0.21.tgz#434f4c67ccda114cdfb19a180b93d9be8bc396be"
+  integrity sha512-/kqtlepLMptX0OgbYD9aMYbM7EFrMZCL7EoHM8Psmg2FuhXoo/bH64KqOiZGGwa6oS9TPdSEDKBnV2LuB8+5vQ==
 
 tmp@^0.0.33:
   version "0.0.33"


Reviews:
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T23:39:35Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T23:39:38Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T23:39:41Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T23:39:44Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T23:39:47Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T23:39:53Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T23:39:59Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T23:40:02Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T23:40:05Z
user: login: kody-ai[bot]
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T00:55:07Z

Review comments:
diff_hunk: @@ -189,11 +189,25 @@ export class RunCodeReviewAutomationUseCase {
     }
 
     private shouldRunAutomation(payload: any, platformType: PlatformType) {
path: src/core/application/use-cases/automation/runCodeReview.use-case.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Maintainability](https://img.shields.io/badge/Maintainability-0091EA) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
private shouldRunAutomation(payload: { action?: string; object_attributes?: { action?: string; state?: string; }; eventType?: string; resource?: { pullRequest?: { status?: string; }; status?: string; }; origin?: string; }, platformType: PlatformType) {
```
Consider adding type safety to the payload parameter in the shouldRunAutomation method to prevent potential runtime errors. Currently, it is typed as 'any', which can lead to issues if the payload structure changes or is incorrect.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-22T23:39:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -189,11 +189,25 @@ export class RunCodeReviewAutomationUseCase {
     }
 
     private shouldRunAutomation(payload: any, platformType: PlatformType) {
-        const allowedActions = ['opened', 'synchronize', 'open', 'update', 'git.pullrequest.updated', 'git.pullrequest.created'];
+        const allowedActions = [
+            'opened',
+            'synchronize',
+            'open',
+            'update',
+            'git.pullrequest.updated',
+            'git.pullrequest.created',
+        ];
         const currentAction =
-            payload?.action || payload?.object_attributes?.action || payload?.eventType;
+            payload?.action ||
+            payload?.object_attributes?.action ||
+            payload?.eventType;
+
+        const isMerged =
+            payload?.object_attributes?.state === 'merged' ||
+            payload?.resource?.pullRequest?.status === 'completed' ||
+            payload?.resource?.status === 'completed' ||
+            false;
 
-        const isMerged = payload?.object_attributes?.state === 'merged';
         const isCommand = payload?.origin === 'command';
 
         // bitbucket has already been handled in the webhook validation
path: src/core/application/use-cases/automation/runCodeReview.use-case.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Maintainability](https://img.shields.io/badge/Maintainability-0091EA) ![low](https://img.shields.io/badge/severity_level-low-1A8EBC)
```typescript
if (!allowedActions.includes(currentAction) || isMerged) { this.logger.debug({ message: 'Automation skipped', context: RunCodeReviewAutomationUseCase.name, metadata: { currentAction, isMerged } }); return false; }
```
Consider adding logging for cases where the automation should not run, to improve debugging and monitoring capabilities.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-22T23:39:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -6,17 +6,13 @@ import { GenerateCodeReviewParameterUseCase } from '../../parameters/generate-co
 import { GenerateKodyRulesUseCase } from '../../kodyRules/generate-kody-rules.use-case';
path: src/core/application/use-cases/platformIntegration/codeManagement/finish-onboarding.use-case.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Potential Issues](https://img.shields.io/badge/Potential_Issues-B71C1C) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
if (!this.request?.user?.organization?.uuid) {
```
Consider adding a type guard for the request.user.organization.uuid to ensure type safety and prevent potential runtime errors.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-22T23:39:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -111,7 +111,9 @@ export class ReceiveWebhookUseCase implements IUseCase {
                     break;
                 case 'ms.vss-code.git-pullrequest-comment-event':
                     const comment = params.payload?.resource.comment.content;
-                    const pullRequestId = params.payload?.resource.pullRequest.status === 'active';
+                    const pullRequestId =
+                        params.payload?.resource?.pullRequest?.status ===
+                        'active';
path: src/core/application/use-cases/platformIntegration/codeManagement/receiveWebhook.use-case.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Potential Issues](https://img.shields.io/badge/Potential_Issues-B71C1C) ![high](https://img.shields.io/badge/severity_level-high-6B6B92)
```typescript
const pullRequestId = params.payload?.resource?.pullRequest && params.payload?.resource?.pullRequest?.status === 'active';
```
The `pullRequestId` check could be more robust by verifying the existence of the pull request object before accessing its status.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-22T23:39:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -27,7 +27,7 @@ export class SavePullRequestUseCase {
         private readonly codeManagement: CodeManagementService,
 
         private readonly logger: PinoLoggerService,
-    ) { }
+    ) {}
 
     public async execute(params: {
         payload: any;
path: src/core/application/use-cases/pullRequests/save.use-case.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Potential Issues](https://img.shields.io/badge/Potential_Issues-B71C1C) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
payload: Record<string, unknown>;
```
Add type safety for the payload parameter to avoid potential runtime errors

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-22T23:39:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,11 +70,21 @@ import { IRepository } from '@/core/domain/pullRequests/interfaces/pullRequests.
 @IntegrationServiceDecorator(PlatformType.BITBUCKET, 'codeManagement')
path: src/core/infrastructure/adapters/services/bitbucket/bitbucket.service.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Maintainability](https://img.shields.io/badge/Maintainability-0091EA) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
Omit<ICodeManagementService, 'getOrganizations' | 'getListOfValidReviews' | 'getUserByEmailOrName' | 'getPullRequestReviewThreads' | 'getUserById' | 'getDataForCalculateDeployFrequency' | 'getCommitsByReleaseMode' | 'getAuthenticationOAuthToken' | 'countReactions' | 'getRepositoryAllFiles'>
```
The Omit interface implementation should include all relevant methods from ICodeManagementService to ensure complete type safety and interface compliance.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-22T23:39:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -103,14 +115,20 @@ export class CheckIfPRCanBeApprovedCronProvider {
                     continue;
                 }
 
-                const codeReviewConfig = codeReviewParameter.configValue as {
-                    global: CodeReviewConfig
-                    repositories: CodeReviewConfigWithRepositoryInfo[]
-                }
-
-                if (!Array.isArray(codeReviewConfig.repositories) || codeReviewConfig.repositories.length < 1) {
+                const codeReviewConfig = codeReviewParameter?.configValue as {
path: src/core/infrastructure/adapters/services/cron/CheckIfPRCanBeApproved.cron.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Maintainability](https://img.shields.io/badge/Maintainability-0091EA) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
function isCodeReviewConfig(value: unknown): value is { global: CodeReviewConfig; repositories: CodeReviewConfigWithRepositoryInfo[] } {
  return value != null && typeof value === 'object' && 'global' in value && 'repositories' in value;
}

const codeReviewConfig = isCodeReviewConfig(codeReviewParameter?.configValue) ? codeReviewParameter.configValue : null;
```
The `codeReviewConfig` type assertion could be more specific. Consider using a proper type guard to ensure the config value is of the expected type.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-22T23:39:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -134,21 +146,7 @@ export class GithubService
 
         private readonly promptService: PromptService,
         private readonly logger: PinoLoggerService,
-    ) { }
-
-
-    getUserById(params: {
-        organizationAndTeamData: OrganizationAndTeamData;
-        userId: string;
-    }): Promise<any | null> {
-        throw new Error('Method not implemented.');
-    }
-    getLanguageRepository(params: any): Promise<any | null> {
-        throw new Error('Method not implemented.');
-    }
-    createSingleIssueComment(params: any): Promise<any | null> {
-        throw new Error('Method not implemented.');
-    }
+    ) {}
path: src/core/infrastructure/adapters/services/github/github.service.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Maintainability](https://img.shields.io/badge/Maintainability-0091EA) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
private async handleIntegration(
  integration: IntegrationEntity,
  authDetails: GithubAuthDetail,
  organizationAndTeamData: OrganizationAndTeamData
): Promise<void> {
```
Add type definitions for the params object to improve type safety and code maintainability.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-22T23:40:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -83,6 +83,9 @@ export class GitlabService
             | 'getRepositoryAllFiles'
             | 'getAuthenticationOAuthToken'
             | 'getPullRequestDetails'
+            | 'getCommitsByReleaseMode'
+            | 'getDataForCalculateDeployFrequency'
+            | 'requestChangesPullRequest'
path: src/core/infrastructure/adapters/services/gitlab.service.ts
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Maintainability](https://img.shields.io/badge/Maintainability-0091EA) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
| 'getCommitsByReleaseMode': (params: { organizationAndTeamData: OrganizationAndTeamData, releaseMode: string }) => Promise<Commit[]>
| 'getDataForCalculateDeployFrequency': (params: { organizationAndTeamData: OrganizationAndTeamData, dateRange: { startDate: Date, endDate: Date } }) => Promise<DeployFrequencyData>
| 'requestChangesPullRequest': (params: { organizationAndTeamData: OrganizationAndTeamData, repository: Repository, prNumber: number, message: string }) => Promise<void>
```
The new method signatures should include proper type definitions instead of implicit 'any' types to ensure type safety and better code maintainability.

<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-04-22T23:40:05Z
author_association: CONTRIBUTOR

PR comments:
user: login: kody-ai[bot]
created_at: 2025-04-22T23:35:57Z
body: ## Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.



<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ❌ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ❌ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ❌ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: CONTRIBUTOR
user: login: kody-ai[bot]
created_at: 2025-04-23T00:52:41Z
body: # Kody Review Complete
**Great news!** 🎉
No issues were found that match your current review configurations.

Keep up the excellent work! 🚀

<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ❌ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ❌ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ❌ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: CONTRIBUTOR