- Three-tier role system: user, admin, readonly_admin with dashboard gating - Client field (Yes/No) on registration with conditional Client Name and Studio Name - Auto-tag client agents as "needs_verification" with Verification tab on admin dashboard - Client agent email notification via Mailgun to configured recipients - Daily agent digest email scheduled via APScheduler (configurable hour) - Manual digest trigger endpoint: POST /api/admin/digest/send - Role dropdown replaces is_admin checkbox in user edit modal - Registration form reordered: Name, Description, Purpose, Client, Client Name, Studio, Tool - Stat card CSS fix for text truncation on admin dashboard - Updated CLAUDE.md documentation and PLAN file Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
435 lines
No EOL
16 KiB
HTML
435 lines
No EOL
16 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Register AI Agent - AgentHub{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container my-5">
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-8 col-lg-6">
|
|
<div class="card shadow-lg border-0">
|
|
<div class="card-body p-5">
|
|
<div class="text-center mb-4">
|
|
<div class="agent-icon mb-3">
|
|
<i class="fas fa-robot"></i>
|
|
</div>
|
|
<h2 class="card-title mb-2">Register AI Agent</h2>
|
|
<p class="text-muted">Create a new AI agent in the system</p>
|
|
</div>
|
|
|
|
{% if error %}
|
|
<div class="alert alert-danger" role="alert">
|
|
<i class="fas fa-exclamation-triangle me-2"></i>{{ error }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<form method="POST" id="agentForm" action="{{ base_path }}/agent-register">
|
|
<div class="mb-4">
|
|
<label for="agentName" class="form-label">
|
|
<i class="fas fa-tag me-2"></i>Agent Name *
|
|
</label>
|
|
<input type="text"
|
|
name="agent_name"
|
|
class="form-control form-control-lg"
|
|
id="agentName"
|
|
placeholder="Enter agent name"
|
|
required>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="agentDescription" class="form-label">
|
|
<i class="fas fa-align-left me-2"></i>Description
|
|
</label>
|
|
<textarea name="agent_description"
|
|
class="form-control"
|
|
id="agentDescription"
|
|
rows="3"
|
|
maxlength="300"
|
|
placeholder="Describe what this agent does"></textarea>
|
|
<div class="form-text">Maximum 300 characters</div>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="agentPurpose" class="form-label">
|
|
<i class="fas fa-bullseye me-2"></i>Purpose
|
|
</label>
|
|
<input type="text"
|
|
name="agent_purpose"
|
|
class="form-control"
|
|
id="agentPurpose"
|
|
maxlength="200"
|
|
placeholder="What is the main purpose of this agent?">
|
|
<div class="form-text">Maximum 200 characters</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-4">
|
|
<label for="agentClient" class="form-label">
|
|
<i class="fas fa-handshake me-2"></i>Client *
|
|
</label>
|
|
<select name="client" class="form-select" id="agentClient" required onchange="toggleClientName()">
|
|
<option value="">Select</option>
|
|
<option value="yes">Yes</option>
|
|
<option value="no">No</option>
|
|
</select>
|
|
<div class="form-text">Is this agent for a client?</div>
|
|
</div>
|
|
<div class="col-md-6 mb-4" id="clientNameSection" style="display: none;">
|
|
<label for="agentClientName" class="form-label">
|
|
<i class="fas fa-building me-2"></i>Client Name *
|
|
</label>
|
|
<input type="text"
|
|
name="client_name"
|
|
class="form-control"
|
|
id="agentClientName"
|
|
placeholder="Enter client name">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="agentStudioName" class="form-label">
|
|
<i class="fas fa-film me-2"></i>Studio Name
|
|
</label>
|
|
<input type="text"
|
|
name="studio_name"
|
|
class="form-control"
|
|
id="agentStudioName"
|
|
placeholder="Enter studio name (optional)">
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="agentTool" class="form-label">
|
|
<i class="fas fa-tools me-2"></i>Tool *
|
|
</label>
|
|
<input type="text"
|
|
name="agent_tool"
|
|
class="form-control form-control-lg"
|
|
id="agentTool"
|
|
placeholder="e.g., chat-sandbox (LibreChat), Copilot, Custom"
|
|
required>
|
|
<div class="form-text">The platform or environment where this agent operates</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-4">
|
|
<label for="agentVersion" class="form-label">
|
|
<i class="fas fa-code-branch me-2"></i>Version
|
|
</label>
|
|
<input type="text"
|
|
name="agent_version"
|
|
class="form-control"
|
|
id="agentVersion"
|
|
placeholder="e.g., 1.0.0">
|
|
</div>
|
|
<div class="col-md-6 mb-4">
|
|
<label for="agentStatus" class="form-label">
|
|
<i class="fas fa-circle me-2"></i>Status
|
|
</label>
|
|
<select name="agent_status" class="form-select" id="agentStatus">
|
|
<option value="Development">Development</option>
|
|
<option value="Active">Active</option>
|
|
<option value="Inactive">Inactive</option>
|
|
<option value="Deprecated">Deprecated</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-4">
|
|
<label for="agentDiscipline" class="form-label">
|
|
<i class="fas fa-layer-group me-2"></i>Discipline *
|
|
</label>
|
|
<select name="discipline" class="form-select" id="agentDiscipline" required>
|
|
<option value="">Select Discipline</option>
|
|
<option value="Strategy">Strategy</option>
|
|
<option value="Creative">Creative</option>
|
|
<option value="Oversight including delivery">Oversight including delivery</option>
|
|
<option value="Optimization">Optimization</option>
|
|
<option value="Back Office including operations">Back Office including operations</option>
|
|
<option value="Pencil Agents">Pencil Agents</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6 mb-4">
|
|
<label for="agentLocation" class="form-label">
|
|
<i class="fas fa-map-marker-alt me-2"></i>Location
|
|
</label>
|
|
<input type="text"
|
|
name="agent_location"
|
|
class="form-control"
|
|
id="agentLocation"
|
|
placeholder="Physical or virtual location">
|
|
</div>
|
|
<div class="col-md-6 mb-4">
|
|
<label for="agentDepartment" class="form-label">
|
|
<i class="fas fa-building me-2"></i>Department
|
|
</label>
|
|
<input type="text"
|
|
name="agent_department"
|
|
class="form-control"
|
|
id="agentDepartment"
|
|
placeholder="Department or team">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="agentContact" class="form-label">
|
|
<i class="fas fa-user-tie me-2"></i>Contact Person
|
|
</label>
|
|
<input type="text"
|
|
name="agent_contact_person"
|
|
class="form-control"
|
|
id="agentContact"
|
|
placeholder="Person responsible for this agent">
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="agentTags" class="form-label">
|
|
<i class="fas fa-tags me-2"></i>Tags
|
|
</label>
|
|
<input type="text"
|
|
name="agent_tags"
|
|
class="form-control"
|
|
id="agentTags"
|
|
placeholder="Enter tags separated by commas (e.g., AI, chatbot, automation)">
|
|
<div class="form-text">Separate multiple tags with commas</div>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="agentUserbase" class="form-label">
|
|
<i class="fas fa-users me-2"></i>Target Userbase
|
|
</label>
|
|
<input type="text"
|
|
name="agent_userbase"
|
|
class="form-control"
|
|
id="agentUserbase"
|
|
placeholder="Target users (e.g., customers, employees, developers)">
|
|
<div class="form-text">Separate multiple groups with commas</div>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="agentCapabilities" class="form-label">
|
|
<i class="fas fa-cogs me-2"></i>Capabilities
|
|
</label>
|
|
<input type="text"
|
|
name="agent_capabilities"
|
|
class="form-control"
|
|
id="agentCapabilities"
|
|
placeholder="Agent capabilities (e.g., data-processing, automated-testing)">
|
|
<div class="form-text">Separate multiple capabilities with commas</div>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<div class="form-check">
|
|
<input class="form-check-input"
|
|
type="checkbox"
|
|
name="quality_audit_status"
|
|
value="true"
|
|
id="qualityAuditStatus"
|
|
{% if not current_user.is_admin %}disabled{% endif %}
|
|
onchange="toggleRiskFactor()">
|
|
<label class="form-check-label" for="qualityAuditStatus">
|
|
<i class="fas fa-certificate me-2"></i>Quality Audit
|
|
{% if current_user.is_admin %}
|
|
<span class="badge bg-warning text-dark ms-2">Admin Only</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary ms-2">Admin Required</span>
|
|
{% endif %}
|
|
</label>
|
|
</div>
|
|
<div class="form-text" id="qualityAuditNote">
|
|
{% if current_user.is_admin %}
|
|
Check this box if the agent has passed quality audit review.
|
|
{% else %}
|
|
Only administrators can mark agents as quality audited.
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-4" id="riskFactorSection" style="display: none;">
|
|
<label for="riskFactor" class="form-label">
|
|
<i class="fas fa-exclamation-triangle me-2"></i>Risk Factor
|
|
<span class="badge bg-warning text-dark ms-2">Admin Only</span>
|
|
<span class="text-danger">*</span>
|
|
</label>
|
|
<select name="risk_factor"
|
|
class="form-select"
|
|
id="riskFactor"
|
|
{% if not current_user.is_admin %}disabled{% endif %}>
|
|
<option value="">Select Risk Level</option>
|
|
<option value="1">1 - Very Low Risk</option>
|
|
<option value="2">2 - Low Risk</option>
|
|
<option value="3">3 - Medium Risk</option>
|
|
<option value="4">4 - High Risk</option>
|
|
<option value="5">5 - Very High Risk</option>
|
|
</select>
|
|
<div class="form-text">
|
|
{% if current_user.is_admin %}
|
|
Required when Quality Audit is checked. Select the appropriate risk level for this agent.
|
|
{% else %}
|
|
Only administrators can set risk factor levels.
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-grid mb-4">
|
|
<button type="submit" class="btn btn-primary btn-lg">
|
|
<i class="fas fa-robot me-2"></i>Register Agent
|
|
</button>
|
|
</div>
|
|
|
|
<div class="text-center">
|
|
<p class="text-muted mb-0">
|
|
<a href="{{ base_path }}/agent-management" class="text-decoration-none fw-bold">
|
|
View My Agents
|
|
</a>
|
|
</p>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.agent-icon {
|
|
width: 80px;
|
|
height: 80px;
|
|
border-radius: 50%;
|
|
background: linear-gradient(135deg, #f3ae3e 0%, #f3ae3e 100%);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 0 auto;
|
|
font-size: 2rem;
|
|
color: white;
|
|
}
|
|
|
|
.form-control-lg {
|
|
padding: 12px 16px;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.card {
|
|
border-radius: 20px;
|
|
backdrop-filter: blur(10px);
|
|
background: rgba(255, 255, 255, 0.95);
|
|
}
|
|
|
|
.form-select {
|
|
padding: 8px 12px;
|
|
}
|
|
|
|
@media (max-width: 576px) {
|
|
.card-body {
|
|
padding: 2rem 1.5rem !important;
|
|
}
|
|
|
|
.agent-icon {
|
|
width: 60px;
|
|
height: 60px;
|
|
font-size: 1.5rem;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
// Simple form validation and duplicate submission prevention
|
|
let formSubmitted = false;
|
|
|
|
function toggleClientName() {
|
|
const clientSelect = document.getElementById('agentClient');
|
|
const clientNameSection = document.getElementById('clientNameSection');
|
|
const clientNameInput = document.getElementById('agentClientName');
|
|
|
|
if (clientSelect.value === 'yes') {
|
|
clientNameSection.style.display = 'block';
|
|
clientNameInput.required = true;
|
|
} else {
|
|
clientNameSection.style.display = 'none';
|
|
clientNameInput.required = false;
|
|
clientNameInput.value = '';
|
|
}
|
|
}
|
|
|
|
function toggleRiskFactor() {
|
|
const qualityAuditStatus = document.getElementById('qualityAuditStatus');
|
|
const riskFactorSection = document.getElementById('riskFactorSection');
|
|
const riskFactor = document.getElementById('riskFactor');
|
|
|
|
if (qualityAuditStatus.checked) {
|
|
riskFactorSection.style.display = 'block';
|
|
riskFactor.required = true;
|
|
} else {
|
|
riskFactorSection.style.display = 'none';
|
|
riskFactor.required = false;
|
|
riskFactor.value = '';
|
|
}
|
|
}
|
|
|
|
document.getElementById('agentForm').addEventListener('submit', function(e) {
|
|
const agentName = document.getElementById('agentName').value.trim();
|
|
const agentTool = document.getElementById('agentTool').value.trim();
|
|
const clientSelect = document.getElementById('agentClient');
|
|
const qualityAuditStatus = document.getElementById('qualityAuditStatus');
|
|
const riskFactor = document.getElementById('riskFactor');
|
|
|
|
if (!agentName) {
|
|
e.preventDefault();
|
|
alert('Agent name is required!');
|
|
return false;
|
|
}
|
|
|
|
if (!agentTool) {
|
|
e.preventDefault();
|
|
alert('Agent tool is required!');
|
|
return false;
|
|
}
|
|
|
|
if (!clientSelect.value) {
|
|
e.preventDefault();
|
|
alert('Client selection is required!');
|
|
return false;
|
|
}
|
|
|
|
if (clientSelect.value === 'yes') {
|
|
const clientName = document.getElementById('agentClientName').value.trim();
|
|
if (!clientName) {
|
|
e.preventDefault();
|
|
alert('Client Name is required when Client is set to Yes!');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const discipline = document.getElementById('agentDiscipline').value;
|
|
if (!discipline) {
|
|
e.preventDefault();
|
|
alert('Discipline is required!');
|
|
return false;
|
|
}
|
|
|
|
// Validate Risk Factor if Quality Audit is checked
|
|
if (qualityAuditStatus.checked && (!riskFactor.value || riskFactor.value === '')) {
|
|
e.preventDefault();
|
|
alert('Risk Factor is required when Quality Audit is checked!');
|
|
return false;
|
|
}
|
|
|
|
// Prevent duplicate submissions
|
|
if (formSubmitted) {
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
|
|
formSubmitted = true;
|
|
|
|
// Disable the submit button to prevent multiple clicks
|
|
const submitBtn = e.target.querySelector('button[type="submit"]');
|
|
if (submitBtn) {
|
|
submitBtn.disabled = true;
|
|
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Registering...';
|
|
}
|
|
|
|
return true;
|
|
});
|
|
</script>
|
|
{% endblock %} |