feat(agnet): persist runtime_execution and sk_access_policy with validation
Go control plane: extend agent plan structs, validate bindings and SK policy codes. Web: surface bindings on Agents page with i18n. Made-with: Cursor
This commit is contained in:
@@ -27,11 +27,27 @@ type agnetSKSource struct {
|
||||
ArtifactID string `json:"artifact_id"`
|
||||
}
|
||||
|
||||
// agnetRuntimeExecution mirrors docs/integration/agnet-platform-api-design.md §5.0 (runtime_execution).
|
||||
type agnetRuntimeExecution struct {
|
||||
ProfileID string `json:"profile_id"`
|
||||
CloudPrincipalRefs []string `json:"cloud_principal_refs"`
|
||||
NetworkPolicyRef string `json:"network_policy_ref"`
|
||||
}
|
||||
|
||||
// agnetSKAccessPolicy mirrors docs/integration/agnet-platform-api-design.md §5.0 (sk_access_policy).
|
||||
type agnetSKAccessPolicy struct {
|
||||
PolicyRef string `json:"policy_ref"`
|
||||
DenySkillIDs []string `json:"deny_skill_ids"`
|
||||
InheritDeploymentDefaults bool `json:"inherit_deployment_defaults"`
|
||||
}
|
||||
|
||||
type agnetAgentPlan struct {
|
||||
RoleTemplate string `json:"role_template"`
|
||||
Goal string `json:"goal"`
|
||||
DefaultModelID string `json:"default_model_id"`
|
||||
SKSources []agnetSKSource `json:"sk_sources"`
|
||||
RoleTemplate string `json:"role_template"`
|
||||
Goal string `json:"goal"`
|
||||
DefaultModelID string `json:"default_model_id"`
|
||||
SKSources []agnetSKSource `json:"sk_sources"`
|
||||
RuntimeExecution agnetRuntimeExecution `json:"runtime_execution"`
|
||||
SKAccessPolicy agnetSKAccessPolicy `json:"sk_access_policy"`
|
||||
}
|
||||
|
||||
type agnetConstraints struct {
|
||||
@@ -132,6 +148,37 @@ func containsString(values []string, target string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func agnetRuntimePartiallySet(r agnetRuntimeExecution) bool {
|
||||
return strings.TrimSpace(r.ProfileID) != "" ||
|
||||
len(r.CloudPrincipalRefs) > 0 ||
|
||||
strings.TrimSpace(r.NetworkPolicyRef) != ""
|
||||
}
|
||||
|
||||
func validateAgentRuntimeBindings(c *gin.Context, agent agnetAgentPlan) bool {
|
||||
r := agent.RuntimeExecution
|
||||
if !agnetRuntimePartiallySet(r) {
|
||||
return true
|
||||
}
|
||||
if strings.TrimSpace(r.ProfileID) == "" {
|
||||
agnetError(c, "RUNTIME_BINDING_INVALID", "runtime_execution.profile_id is required when runtime bindings are present")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func validateAgentSKAccessPolicy(c *gin.Context, agent agnetAgentPlan) bool {
|
||||
p := agent.SKAccessPolicy
|
||||
hasDeny := len(p.DenySkillIDs) > 0
|
||||
if !hasDeny {
|
||||
return true
|
||||
}
|
||||
if strings.TrimSpace(p.PolicyRef) == "" && !p.InheritDeploymentDefaults {
|
||||
agnetError(c, "SK_POLICY_REJECTED", "sk_access_policy.policy_ref or inherit_deployment_defaults is required when deny_skill_ids is set")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func validateOrchestrationPlan(c *gin.Context, plan agnetOrchestrationPlan) bool {
|
||||
if strings.TrimSpace(plan.IntentID) == "" ||
|
||||
strings.TrimSpace(plan.TemplateHint) == "" ||
|
||||
@@ -187,6 +234,12 @@ func validateOrchestrationPlan(c *gin.Context, plan agnetOrchestrationPlan) bool
|
||||
agnetError(c, "MODEL_NOT_ALLOWED", "agent default_model_id is outside allowed_model_ids")
|
||||
return false
|
||||
}
|
||||
if !validateAgentRuntimeBindings(c, agent) {
|
||||
return false
|
||||
}
|
||||
if !validateAgentSKAccessPolicy(c, agent) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
import { api } from '@/lib/api'
|
||||
|
||||
/** Sub-agent cloud/runtime binding (passed to Agnet on deploy). */
|
||||
export type AgnetRuntimeExecution = {
|
||||
profile_id?: string
|
||||
cloud_principal_refs?: string[]
|
||||
network_policy_ref?: string
|
||||
}
|
||||
|
||||
/** SK allow/deny policy attached to the agent in the deployment plan. */
|
||||
export type AgnetSKAccessPolicy = {
|
||||
policy_ref?: string
|
||||
deny_skill_ids?: string[]
|
||||
inherit_deployment_defaults?: boolean
|
||||
}
|
||||
|
||||
export type AgnetDeployment = {
|
||||
deployment_id: string
|
||||
status: string
|
||||
@@ -14,6 +28,8 @@ export type AgnetDeployment = {
|
||||
goal?: string
|
||||
default_model_id?: string
|
||||
sk_sources?: Array<{ type?: string; artifact_id?: string }>
|
||||
runtime_execution?: AgnetRuntimeExecution
|
||||
sk_access_policy?: AgnetSKAccessPolicy
|
||||
}>
|
||||
metadata?: {
|
||||
tenant_id?: string
|
||||
|
||||
@@ -42,6 +42,8 @@ import {
|
||||
getAgnetSnapshots,
|
||||
listAgnetDeployments,
|
||||
type AgnetDeployment,
|
||||
type AgnetRuntimeExecution,
|
||||
type AgnetSKAccessPolicy,
|
||||
} from './api'
|
||||
|
||||
type StatusKey = 'running' | 'success' | 'failed' | 'pending'
|
||||
@@ -820,6 +822,24 @@ export function AgnetTemplatesPage() {
|
||||
)
|
||||
}
|
||||
|
||||
function runtimeSummary(rt: AgnetRuntimeExecution | undefined): boolean {
|
||||
if (!rt) return false
|
||||
return Boolean(
|
||||
(rt.profile_id && rt.profile_id.trim() !== '') ||
|
||||
(rt.cloud_principal_refs && rt.cloud_principal_refs.length > 0) ||
|
||||
(rt.network_policy_ref && rt.network_policy_ref.trim() !== '')
|
||||
)
|
||||
}
|
||||
|
||||
function policySummary(p: AgnetSKAccessPolicy | undefined): boolean {
|
||||
if (!p) return false
|
||||
return Boolean(
|
||||
(p.policy_ref && p.policy_ref.trim() !== '') ||
|
||||
(p.deny_skill_ids && p.deny_skill_ids.length > 0) ||
|
||||
p.inherit_deployment_defaults
|
||||
)
|
||||
}
|
||||
|
||||
export function AgnetAgentsPage() {
|
||||
const { t } = useTranslation()
|
||||
const { data = [] } = useQuery({
|
||||
@@ -835,6 +855,8 @@ export function AgnetAgentsPage() {
|
||||
role: agent.role_template || '-',
|
||||
model: agent.default_model_id || '-',
|
||||
goal: agent.goal || '-',
|
||||
runtime: agent.runtime_execution,
|
||||
skPolicy: agent.sk_access_policy,
|
||||
}))
|
||||
),
|
||||
[data]
|
||||
@@ -861,6 +883,80 @@ export function AgnetAgentsPage() {
|
||||
{row.dep} · model: {row.model}
|
||||
</p>
|
||||
<p className='mt-2 text-sm'>{row.goal}</p>
|
||||
{runtimeSummary(row.runtime) && (
|
||||
<div className='mt-3 rounded-lg border border-dashed border-[color-mix(in_oklch,var(--primary)_22%,var(--border))] bg-[color-mix(in_oklch,var(--card)_40%,transparent)] p-2 text-xs leading-relaxed text-muted-foreground'>
|
||||
<p className='font-semibold text-foreground'>
|
||||
{t('Runtime binding')}
|
||||
</p>
|
||||
{row.runtime?.profile_id ? (
|
||||
<p className='mt-1'>
|
||||
<span className='text-muted-foreground'>
|
||||
{t('Execution profile')}
|
||||
:{' '}
|
||||
</span>
|
||||
<span className='font-mono text-foreground'>
|
||||
{row.runtime.profile_id}
|
||||
</span>
|
||||
</p>
|
||||
) : null}
|
||||
{(row.runtime?.cloud_principal_refs?.length ?? 0) > 0 ? (
|
||||
<p className='mt-1'>
|
||||
<span className='text-muted-foreground'>
|
||||
{t('Cloud principals')}
|
||||
:{' '}
|
||||
</span>
|
||||
<span className='font-mono text-foreground'>
|
||||
{row.runtime?.cloud_principal_refs?.join(', ')}
|
||||
</span>
|
||||
</p>
|
||||
) : null}
|
||||
{row.runtime?.network_policy_ref ? (
|
||||
<p className='mt-1'>
|
||||
<span className='text-muted-foreground'>
|
||||
{t('Network policy')}
|
||||
:{' '}
|
||||
</span>
|
||||
<span className='font-mono text-foreground'>
|
||||
{row.runtime.network_policy_ref}
|
||||
</span>
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
{policySummary(row.skPolicy) && (
|
||||
<div className='mt-3 rounded-lg border border-dashed border-[color-mix(in_oklch,var(--primary)_22%,var(--border))] bg-[color-mix(in_oklch,var(--card)_40%,transparent)] p-2 text-xs leading-relaxed text-muted-foreground'>
|
||||
<p className='font-semibold text-foreground'>
|
||||
{t('SK access policy')}
|
||||
</p>
|
||||
{row.skPolicy?.policy_ref ? (
|
||||
<p className='mt-1'>
|
||||
<span className='text-muted-foreground'>
|
||||
{t('Policy ref')}
|
||||
:{' '}
|
||||
</span>
|
||||
<span className='font-mono text-foreground'>
|
||||
{row.skPolicy.policy_ref}
|
||||
</span>
|
||||
</p>
|
||||
) : null}
|
||||
{(row.skPolicy?.deny_skill_ids?.length ?? 0) > 0 ? (
|
||||
<p className='mt-1'>
|
||||
<span className='text-muted-foreground'>
|
||||
{t('Denied skills')}
|
||||
:{' '}
|
||||
</span>
|
||||
<span className='font-mono text-foreground'>
|
||||
{row.skPolicy?.deny_skill_ids?.join(', ')}
|
||||
</span>
|
||||
</p>
|
||||
) : null}
|
||||
{row.skPolicy?.inherit_deployment_defaults ? (
|
||||
<p className='mt-1 text-muted-foreground'>
|
||||
{t('Inherits deployment defaults')}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -201,6 +201,14 @@
|
||||
"Agent ID *": "Agent ID *",
|
||||
"Agentic development control plane": "Agentic development control plane",
|
||||
"Agents": "Agents",
|
||||
"Runtime binding": "Runtime binding",
|
||||
"SK access policy": "SK access policy",
|
||||
"Execution profile": "Execution profile",
|
||||
"Cloud principals": "Cloud principals",
|
||||
"Network policy": "Network policy",
|
||||
"Policy ref": "Policy ref",
|
||||
"Denied skills": "Denied skills",
|
||||
"Inherits deployment defaults": "Inherits deployment defaults",
|
||||
"Aggregated usage metrics and trend charts.": "Aggregated usage metrics and trend charts.",
|
||||
"aggregates 50+ AI providers behind one unified API. Manage access, track costs, and scale effortlessly.": "aggregates 50+ AI providers behind one unified API. Manage access, track costs, and scale effortlessly.",
|
||||
"Agile Minimal": "Agile Minimal",
|
||||
|
||||
@@ -201,6 +201,14 @@
|
||||
"Agent ID *": "ID d'agent *",
|
||||
"Agentic development control plane": "Agentic development control plane",
|
||||
"Agents": "Agents",
|
||||
"Runtime binding": "Runtime binding",
|
||||
"SK access policy": "SK access policy",
|
||||
"Execution profile": "Execution profile",
|
||||
"Cloud principals": "Cloud principals",
|
||||
"Network policy": "Network policy",
|
||||
"Policy ref": "Policy ref",
|
||||
"Denied skills": "Denied skills",
|
||||
"Inherits deployment defaults": "Inherits deployment defaults",
|
||||
"Aggregated usage metrics and trend charts.": "Métriques d'utilisation agrégées et graphiques de tendances.",
|
||||
"aggregates 50+ AI providers behind one unified API. Manage access, track costs, and scale effortlessly.": "agrège plus de 50 fournisseurs IA derrière une API unifiée. Gérez l'accès, suivez les coûts et évoluez sans effort.",
|
||||
"Agile Minimal": "Agile Minimal",
|
||||
|
||||
@@ -201,6 +201,14 @@
|
||||
"Agent ID *": "エージェントID *",
|
||||
"Agentic development control plane": "Agentic development control plane",
|
||||
"Agents": "Agents",
|
||||
"Runtime binding": "Runtime binding",
|
||||
"SK access policy": "SK access policy",
|
||||
"Execution profile": "Execution profile",
|
||||
"Cloud principals": "Cloud principals",
|
||||
"Network policy": "Network policy",
|
||||
"Policy ref": "Policy ref",
|
||||
"Denied skills": "Denied skills",
|
||||
"Inherits deployment defaults": "Inherits deployment defaults",
|
||||
"Aggregated usage metrics and trend charts.": "集計された使用量メトリクスとトレンドチャート。",
|
||||
"aggregates 50+ AI providers behind one unified API. Manage access, track costs, and scale effortlessly.": "50以上のAIプロバイダーを統一APIで集約。アクセス管理、コスト追跡、スケーリングを簡単に。",
|
||||
"Agile Minimal": "Agile Minimal",
|
||||
|
||||
@@ -201,6 +201,14 @@
|
||||
"Agent ID *": "Идентификатор агента *",
|
||||
"Agentic development control plane": "Agentic development control plane",
|
||||
"Agents": "Agents",
|
||||
"Runtime binding": "Runtime binding",
|
||||
"SK access policy": "SK access policy",
|
||||
"Execution profile": "Execution profile",
|
||||
"Cloud principals": "Cloud principals",
|
||||
"Network policy": "Network policy",
|
||||
"Policy ref": "Policy ref",
|
||||
"Denied skills": "Denied skills",
|
||||
"Inherits deployment defaults": "Inherits deployment defaults",
|
||||
"Aggregated usage metrics and trend charts.": "Агрегированные метрики использования и графики трендов.",
|
||||
"aggregates 50+ AI providers behind one unified API. Manage access, track costs, and scale effortlessly.": "объединяет 50+ ИИ-провайдеров за единым API. Управляйте доступом, отслеживайте затраты и масштабируйтесь без усилий.",
|
||||
"Agile Minimal": "Agile Minimal",
|
||||
|
||||
@@ -201,6 +201,14 @@
|
||||
"Agent ID *": "Mã đại lý *",
|
||||
"Agentic development control plane": "Agentic development control plane",
|
||||
"Agents": "Agents",
|
||||
"Runtime binding": "Runtime binding",
|
||||
"SK access policy": "SK access policy",
|
||||
"Execution profile": "Execution profile",
|
||||
"Cloud principals": "Cloud principals",
|
||||
"Network policy": "Network policy",
|
||||
"Policy ref": "Policy ref",
|
||||
"Denied skills": "Denied skills",
|
||||
"Inherits deployment defaults": "Inherits deployment defaults",
|
||||
"Aggregated usage metrics and trend charts.": "Chỉ số sử dụng tổng hợp và biểu đồ xu hướng.",
|
||||
"aggregates 50+ AI providers behind one unified API. Manage access, track costs, and scale effortlessly.": "tổng hợp hơn 50 nhà cung cấp AI sau một API thống nhất. Quản lý truy cập, theo dõi chi phí và mở rộng dễ dàng.",
|
||||
"Agile Minimal": "Agile Minimal",
|
||||
|
||||
@@ -201,6 +201,14 @@
|
||||
"Agent ID *": "代理 ID *",
|
||||
"Agentic development control plane": "智能体研发控制面",
|
||||
"Agents": "Agent",
|
||||
"Runtime binding": "运行时绑定",
|
||||
"SK access policy": "SK 访问策略",
|
||||
"Execution profile": "执行档案",
|
||||
"Cloud principals": "云身份引用",
|
||||
"Network policy": "网络策略",
|
||||
"Policy ref": "策略引用",
|
||||
"Denied skills": "禁止的技能",
|
||||
"Inherits deployment defaults": "继承部署默认策略",
|
||||
"Aggregated usage metrics and trend charts.": "聚合使用指标和趋势图表。",
|
||||
"aggregates 50+ AI providers behind one unified API. Manage access, track costs, and scale effortlessly.": "聚合 50+ AI 提供商于统一 API 之后。轻松管理访问、追踪成本、弹性扩展。",
|
||||
"Agile Minimal": "敏捷最小编队",
|
||||
|
||||
Reference in New Issue
Block a user