Skip to main content

OSSA Agents + GitLab Integration

OSSA Agents + GitLab Integration

Overview

20 OSSA agents with GitLab service accounts operate as first-class GitLab citizens.

Each agent:

  • Has a dedicated service account
  • Authenticates with GitLab API token
  • Can read/write repos, MRs, issues, pipelines
  • Reports telemetry to GitLab Ultimate
  • Integrates with GitLab Duo (AI platform)

Architecture

GitLab Event (Webhook)
  ↓
Agent Mesh (Router)
  ↓
OSSA Agent (Service Account)
  ↓ GitLab API
  ├─ Read: Issues, MRs, Code, Pipelines
  ├─ Write: Comments, Commits, MRs, Labels
  ├─ Execute: CI/CD, Deployments
  └─ Report: Telemetry, Alerts, Metrics

Service Accounts (20 Total)

Location: service-account-mapping.json

By Role:

  • ANALYZER (6): ossa-validator, drupal-standards-checker, vulnerability-scanner, cost-intelligence-monitor
  • REVIEWER (2): mr-reviewer, code-quality-reviewer
  • ORCHESTRATOR (3): task-dispatcher, release-coordinator, issue-lifecycle-manager
  • EXECUTOR (9): pipeline-remediation, module-generator, recipe-publisher, cluster-operator, kagent-catalog-sync, mcp-server-builder, documentation-aggregator, k8s-ops-worker, kagent-worker
  • WORKER (2): ci-fixer-worker, drupal-standards-worker

Each has:

{ "agent_id": "mr-reviewer", "service_account_id": 32706577, "service_account_username": "agent-mr-reviewer", "token_var": "SA_TOKEN_MR_REVIEWER", "role": "REVIEWER", "status": "mapped" }

Authentication Pattern

TypeScript SDK

import { Gitlab } from '@gitbeaker/rest'; class OSSAAgent { private gitlab: InstanceType<typeof Gitlab>; constructor(agentId: string) { const mapping = loadServiceAccountMapping(); const agent = mapping.agents.find(a => a.agent_id === agentId); if (!agent || agent.status !== 'mapped') { throw new Error(`Agent ${agentId} not mapped`); } const token = process.env[agent.token_var]; if (!token) { throw new Error(`Missing token: ${agent.token_var}`); } this.gitlab = new Gitlab({ host: 'https://gitlab.com', token: token, }); } async listMyProjects() { return await this.gitlab.Projects.all({ owned: true }); } }

Use Cases

1. MR Reviewer Agent

Trigger: MR opened/updated Actions:

  • Fetches MR diff via GitLab API
  • Analyzes code with Claude/GPT-4
  • Posts review comments
  • Approves or requests changes
// packages/@ossa/mr-reviewer/src/index.ts import { Gitlab } from '@gitbeaker/rest'; class MRReviewerAgent { private gitlab: InstanceType<typeof Gitlab>; constructor() { this.gitlab = new Gitlab({ token: process.env.SA_TOKEN_MR_REVIEWER, }); } async reviewMR(projectId: number, mrIid: number) { // 1. Fetch MR details const mr = await this.gitlab.MergeRequests.show(projectId, mrIid); // 2. Get changes const changes = await this.gitlab.MergeRequests.changes(projectId, mrIid); // 3. Analyze with LLM const review = await this.analyzeDiff(changes.changes); // 4. Post review await this.gitlab.MergeRequestNotes.create(projectId, mrIid, { body: this.formatReview(review), }); // 5. Update labels if (review.hasIssues) { await this.gitlab.MergeRequests.edit(projectId, mrIid, { add_labels: 'needs-changes', }); } else { await this.gitlab.MergeRequests.edit(projectId, mrIid, { add_labels: 'approved-by-agent', }); } } }

2. CI Fixer Worker

Trigger: Pipeline failed Actions:

  • Analyzes pipeline failure logs
  • Identifies root cause
  • Creates fix commit
  • Pushes to branch
// packages/@ossa/ci-fixer-worker/src/index.ts import { Gitlab } from '@gitbeaker/rest'; class CIFixerAgent { private gitlab: InstanceType<typeof Gitlab>; constructor() { this.gitlab = new Gitlab({ token: process.env.SA_TOKEN_CI_FIXER_WORKER, }); } async fixPipeline(projectId: number, pipelineId: number) { // 1. Get pipeline details const pipeline = await this.gitlab.Pipelines.show(projectId, pipelineId); // 2. Get failed jobs const jobs = await this.gitlab.Jobs.showPipelineJobs(projectId, pipelineId, { scope: ['failed'], }); // 3. Analyze failure logs for (const job of jobs) { const trace = await this.gitlab.Jobs.showLog(projectId, job.id); const fix = await this.analyzeLogs(trace); if (fix) { // 4. Create fix commit await this.gitlab.Commits.create(projectId, pipeline.ref, { commit_message: `fix(ci): ${fix.description}`, actions: [{ action: 'update', file_path: fix.filePath, content: fix.content, }], }); } } } }

3. Issue Lifecycle Manager

Trigger: Issue created/labeled Actions:

  • Analyzes issue description
  • Creates implementation plan
  • Creates branch and MR
  • Assigns to appropriate team
// packages/@ossa/issue-lifecycle-manager/src/index.ts import { Gitlab } from '@gitbeaker/rest'; class IssueLifecycleAgent { private gitlab: InstanceType<typeof Gitlab>; constructor() { this.gitlab = new Gitlab({ token: process.env.SA_TOKEN_ISSUE_LIFECYCLE_MANAGER, }); } async processIssue(projectId: number, issueIid: number) { // 1. Fetch issue const issue = await this.gitlab.Issues.show(projectId, issueIid); // 2. Analyze with LLM const analysis = await this.analyzeIssue(issue); // 3. Post analysis comment await this.gitlab.IssueNotes.create(projectId, issueIid, { body: this.formatAnalysis(analysis), }); // 4. Create branch const branchName = `${issueIid}-${issue.title.toLowerCase().replace(/\s+/g, '-')}`; await this.gitlab.Branches.create(projectId, branchName, 'main'); // 5. Create MR const mr = await this.gitlab.MergeRequests.create(projectId, branchName, 'main', issue.title, { description: `Closes #${issueIid}\n\n${analysis.implementationPlan}`, }); // 6. Link issue to MR await this.gitlab.Issues.edit(projectId, issueIid, { add_labels: 'in-progress', }); } }

4. Vulnerability Scanner

Trigger: Code pushed, scheduled Actions:

  • Scans code for vulnerabilities
  • Creates security issues
  • Updates security dashboard
// packages/@ossa/vulnerability-scanner/src/index.ts import { Gitlab } from '@gitbeaker/rest'; class VulnerabilityScannerAgent { private gitlab: InstanceType<typeof Gitlab>; constructor() { this.gitlab = new Gitlab({ token: process.env.SA_TOKEN_VULNERABILITY_SCANNER, }); } async scanProject(projectId: number) { // 1. Get latest commit const commits = await this.gitlab.Commits.all(projectId, { per_page: 1 }); const latestCommit = commits[0]; // 2. Get file tree const tree = await this.gitlab.Repositories.tree(projectId, { ref: latestCommit.id, recursive: true, }); // 3. Scan each file const vulnerabilities = []; for (const file of tree) { if (file.type === 'blob') { const content = await this.gitlab.RepositoryFiles.show( projectId, file.path, latestCommit.id, ); const vulns = await this.scanFileContent( Buffer.from(content.content, 'base64').toString(), file.path, ); vulnerabilities.push(...vulns); } } // 4. Create issues for vulnerabilities for (const vuln of vulnerabilities) { await this.gitlab.Issues.create(projectId, { title: `Security: ${vuln.title}`, description: this.formatVulnerability(vuln), labels: ['security', `severity::${vuln.severity}`], confidential: true, }); } // 5. Report to GitLab Security Dashboard // (via GitLab Security Report format) } }

5. Release Coordinator

Trigger: Tag pushed, milestone complete Actions:

  • Generates changelog
  • Creates release notes
  • Triggers deployments
  • Updates docs
// packages/@ossa/release-coordinator/src/index.ts import { Gitlab } from '@gitbeaker/rest'; class ReleaseCoordinatorAgent { private gitlab: InstanceType<typeof Gitlab>; constructor() { this.gitlab = new Gitlab({ token: process.env.SA_TOKEN_RELEASE_COORDINATOR, }); } async createRelease(projectId: number, tagName: string) { // 1. Get commits since last release const tags = await this.gitlab.Tags.all(projectId); const previousTag = tags[1]; // Assuming sorted const commits = await this.gitlab.Commits.all(projectId, { ref_name: tagName, since: previousTag.commit.created_at, }); // 2. Generate changelog const changelog = await this.generateChangelog(commits); // 3. Create release await this.gitlab.Releases.create(projectId, { tag_name: tagName, name: `Release ${tagName}`, description: changelog, }); // 4. Close milestone const milestones = await this.gitlab.ProjectMilestones.all(projectId, { title: tagName, }); if (milestones.length > 0) { await this.gitlab.ProjectMilestones.edit(projectId, milestones[0].id, { state_event: 'close', }); } // 5. Trigger deployment pipeline await this.gitlab.Pipelines.create(projectId, tagName, { variables: [{ key: 'RELEASE', value: tagName }], }); } }

GitLab API Capabilities (Per Agent)

Read Operations (All Agents)

  • Projects, Groups, Users
  • Issues, MRs, Comments
  • Commits, Branches, Tags
  • Pipelines, Jobs, Artifacts
  • Files, Repository tree

Write Operations (By Role)

ANALYZER (Read-only + Comments):

  • Post comments on issues/MRs
  • Create security issues

REVIEWER (Analysis + Approval):

  • Post review comments
  • Approve/unapprove MRs
  • Add/remove labels

ORCHESTRATOR (Coordination):

  • Create/update issues
  • Create/update MRs
  • Manage milestones
  • Trigger pipelines

EXECUTOR (Full Write):

  • Create commits
  • Create branches
  • Push code
  • Merge MRs
  • Create releases

WORKER (Background Tasks):

  • Process CI/CD jobs
  • Update statuses
  • Sync external data

Webhook Integration

Webhook Receiver

// infrastructure/webhook-receiver/src/server.ts import express from 'express'; import { verifyGitLabWebhook } from './auth.js'; import { routeToAgent } from './router.js'; const app = express(); app.use(express.json()); app.post('/webhooks/gitlab', verifyGitLabWebhook, async (req, res) => { const eventType = req.headers['x-gitlab-event']; const payload = req.body; // Route to appropriate agent const agentId = determineAgent(eventType, payload); // Trigger agent via agent-mesh await routeToAgent(agentId, { event: eventType, payload: payload, }); res.json({ status: 'received' }); });

Event Routing

MR Eventsmr-reviewer, code-quality-reviewer Pipeline Eventsci-fixer-worker, pipeline-remediation Issue Eventsissue-lifecycle-manager Tag Eventsrelease-coordinator Push Eventsvulnerability-scanner, drupal-standards-checker

GitLab Ultimate Integration

1. Observability

Each agent reports:

  • OpenTelemetry traces
  • Alerts for failures
  • Metrics for operations
import { GitLabObservability } from '@bluefly/agent-registry'; const obs = new GitLabObservability({ gitlabUrl: 'https://gitlab.com', projectId: process.env.GITLAB_PROJECT_ID, serviceAccountToken: process.env.SA_TOKEN_MR_REVIEWER, agentName: 'mr-reviewer', agentRole: 'REVIEWER', }); // Export trace await obs.exportTrace({ traceId: '...', spanId: '...', name: 'review_mr', // ... OTLP format }); // Create alert await obs.createAlert({ title: 'MR Review Failed', severity: 'high', service: 'mr-reviewer', });

2. Service Catalog

Register all agents:

npm run gitlab:register-services

3. Error Tracking

Agents report errors to Sentry (via GitLab):

await obs.reportError(error, { context: 'review_mr' });

GitLab Duo Integration

Code Suggestions

// Use Duo for code generation const suggestions = await this.gitlab.Duo.suggestCode({ context: fileContent, instruction: 'Add error handling', });

Chat API

// Ask Duo for analysis const response = await this.gitlab.Duo.chat({ message: 'Explain this vulnerability', context: vulnerabilityDetails, });

Best Practices

  1. Token Security: Store tokens in environment variables, never in code
  2. Rate Limiting: Respect GitLab API rate limits (2000 req/min)
  3. Error Handling: Retry transient failures, log permanent errors
  4. Idempotency: Design operations to be idempotent
  5. Permissions: Use least-privilege access for each agent role
  6. Audit Logs: All operations logged to GitLab Ultimate observability
  7. A2A Communication: Inter-agent coordination via A2A collector

Environment Variables

# Service Account Tokens (20 total) SA_TOKEN_MR_REVIEWER=glpat-xxx SA_TOKEN_CI_FIXER_WORKER=glpat-xxx SA_TOKEN_ISSUE_LIFECYCLE_MANAGER=glpat-xxx # ... (17 more) # GitLab Configuration GITLAB_URL=https://gitlab.com GITLAB_PROJECT_ID=your-project-id # A2A Collector (for inter-agent communication) GITLAB_COLLECTOR_TOKEN=glpat-xxx

Next Steps

  1. Deploy A2A Collector with GitLab integration
  2. Register agents in service catalog
  3. Configure webhooks to trigger agents
  4. Enable GitLab Duo for enhanced AI capabilities
  5. Set up dashboards in GitLab Ultimate observability

All agents = TypeScript, Zod, DRY, SOLID, API-First. Zero .sh files.