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:
Ubuntu
2026-05-01 08:13:21 +00:00
parent b42e5b2dd0
commit 4e8b4a66c6
9 changed files with 217 additions and 4 deletions
+57 -4
View File
@@ -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
+16
View File
@@ -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>
+8
View File
@@ -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",
+8
View File
@@ -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",
+8
View File
@@ -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",
+8
View File
@@ -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",
+8
View File
@@ -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",
+8
View File
@@ -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": "敏捷最小编队",