feat(agnet): SK repo_ref validation, create deployment UI, docs touch-ups

Extend agnet SK sources with repo_ref and snapshot display; add authenticated
deployment sheet + API types; cockpit toolbar entry; locale strings; minor docs.

Made-with: Cursor
This commit is contained in:
Ubuntu
2026-05-01 09:12:00 +00:00
parent 356592e294
commit b0acfd44c1
12 changed files with 958 additions and 34 deletions
@@ -124,6 +124,10 @@ Tenant(租户)
下列条款为 **Heicode 与 Agnet 联合落地时必须写清** 的契约;API 形状可与 `**POST /deployments`** 合一或拆为 `**POST /teams/deployments`** 等聚合端点,但 **语义不得缩水**。
**启动参数与权限归属(与 Manager 的边界)**
- **Agnet 在拉起编队 / 子 agent 运行时**(进程或等价隔离单元)须获得完整部署参数:`sk_sources`、`runtime_execution`、`sk_access_policy`、成员与模型声明等;**不得在缺少参数时静默放宽为越权默认**。
- **权限与策略的可执行副本落在 Agnet**:Git 连接、`cloud_principal_refs`、SK 允许/拒绝边界由 Agnet 控制面 **落账并在运行时强制执行**;Heicode Manager **只负责发起部署请求并展示 Agnet 回传的快照锚点与观测字段**,**不是**运行时的权限裁决引擎。
| 契约项 | 要求 |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+1 -2
View File
@@ -120,8 +120,7 @@ Agnet 不需要、也不应该提供针对 SK 正文的 `PUT` / `PATCH`:写操
- 团队成员列表与组织内角色
- 各成员所用模型 / `provider_profile_id`
- 子 agent 模板与 `sk_sources`
- **用户在 Manager 配置的子 agent 云上 / 运行时权限**(如 `runtime_execution`:VM 池、云 principal、网络策略等),**必须随部署请求传给 Agnet**,不得在链路中丢弃
- **SK 允许 / 禁止策略**(如 `sk_access_policy`),与快照合并后的生效边界须由平台落账,子 agent 仅能在该边界内消费 SK
- **子 agent 云上 / 运行时权限等参数**(如 `runtime_execution`)与 **SK 允许 / 禁止策略**(如 `sk_access_policy`)须 **在 Agnet 拉起编队/运行时随部署请求传入**;**生效副本落在 Agnet**,由其运行时强制执行;Manager 仅透传配置并展示回传锚点,不作执行时代替
部署完成后 Manager 应能展示:
+70 -7
View File
@@ -22,9 +22,19 @@ type agnetBudget struct {
MaxDurationSec int `json:"max_duration_sec"`
}
// agnetRepoRef matches docs/integration/orchestration-plan-contract.md (git sk_sources).
type agnetRepoRef struct {
ConnectionID string `json:"connection_id"`
RepoURL string `json:"repo_url"`
Ref string `json:"ref"`
Paths []string `json:"paths"`
}
type agnetSKSource struct {
Type string `json:"type"`
ArtifactID string `json:"artifact_id"`
Type string `json:"type"`
ArtifactID string `json:"artifact_id"`
Mime string `json:"mime"`
RepoRef agnetRepoRef `json:"repo_ref"`
}
// agnetRuntimeExecution mirrors docs/integration/agnet-platform-api-design.md §5.0 (runtime_execution).
@@ -166,6 +176,58 @@ func validateAgentRuntimeBindings(c *gin.Context, agent agnetAgentPlan) bool {
return true
}
func skSourceDisplayRef(s agnetSKSource) string {
switch strings.TrimSpace(s.Type) {
case "git":
r := s.RepoRef
ref := strings.TrimSpace(r.Ref)
pathPart := strings.Join(r.Paths, ",")
if ref != "" && pathPart != "" {
return ref + ":" + pathPart
}
if ref != "" {
return ref
}
return "git"
case "upload":
id := strings.TrimSpace(s.ArtifactID)
if id != "" {
return id
}
return "upload"
default:
return strings.TrimSpace(s.ArtifactID)
}
}
func validateSKSourceEntry(c *gin.Context, source agnetSKSource) bool {
sourceType := strings.TrimSpace(source.Type)
if sourceType == "" {
return true
}
switch sourceType {
case "git":
ref := source.RepoRef
if strings.TrimSpace(ref.Ref) == "" {
agnetError(c, "SK_SOURCE_UNRESOLVABLE", "git sk_sources.repo_ref.ref is required")
return false
}
if len(ref.Paths) == 0 {
agnetError(c, "SK_SOURCE_UNRESOLVABLE", "git sk_sources.repo_ref.paths must not be empty")
return false
}
case "upload":
if strings.TrimSpace(source.ArtifactID) == "" {
agnetError(c, "SK_SOURCE_UNRESOLVABLE", "upload sk_sources.artifact_id is required")
return false
}
default:
agnetError(c, "SK_SOURCE_UNRESOLVABLE", "unsupported sk source type")
return false
}
return true
}
func validateAgentSKAccessPolicy(c *gin.Context, agent agnetAgentPlan) bool {
p := agent.SKAccessPolicy
hasDeny := len(p.DenySkillIDs) > 0
@@ -223,9 +285,7 @@ func validateOrchestrationPlan(c *gin.Context, plan agnetOrchestrationPlan) bool
return false
}
for _, source := range agent.SKSources {
sourceType := strings.TrimSpace(source.Type)
if sourceType != "" && sourceType != "git" && sourceType != "upload" {
agnetError(c, "SK_SOURCE_UNRESOLVABLE", "unsupported sk source type")
if !validateSKSourceEntry(c, source) {
return false
}
}
@@ -449,10 +509,13 @@ func AgnetResolveSKSnapshots(c *gin.Context) {
if sourceType == "" {
continue
}
sourceRef := strings.TrimSpace(source.ArtifactID)
if sourceRef == "" {
sourceRef := skSourceDisplayRef(source)
if strings.TrimSpace(sourceRef) == "" {
sourceRef = "ref_" + common.GetUUID()[:8]
}
if sourceType == "git" {
sourceRef = sourceRef + "@sha_" + common.GetUUID()[:12]
}
snapshots = append(snapshots, agnetSKSnapshot{
SnapshotID: "sks_" + common.GetUUID()[:12],
DeploymentID: deploymentID,
+86 -13
View File
@@ -14,6 +14,68 @@ export type AgnetSKAccessPolicy = {
inherit_deployment_defaults?: boolean
}
/** Git-backed SK source (`type: git`). */
export type AgnetRepoRef = {
connection_id?: string
repo_url?: string
ref: string
paths: string[]
}
/** Single SK source entry (git or upload). */
export type AgnetSKSource = {
type?: string
artifact_id?: string
mime?: string
repo_ref?: AgnetRepoRef
}
export type AgnetAgentPlan = {
role_template: string
goal: string
default_model_id?: string
sk_sources?: AgnetSKSource[]
runtime_execution?: AgnetRuntimeExecution
sk_access_policy?: AgnetSKAccessPolicy
}
export type AgnetBudget = {
max_tokens: number
max_cost_usd: number
max_duration_sec: number
}
export type AgnetConstraints = {
allowed_model_ids?: string[]
}
export type AgnetOrchestrationMetadata = {
tenant_id: string
project_id: string
correlation_id: string
}
export type AgnetOrchestrationPlan = {
intent_id: string
template_hint: string
objective: string
risk_level: 'low' | 'medium' | 'high'
budget: AgnetBudget
agents: AgnetAgentPlan[]
constraints: AgnetConstraints
metadata: AgnetOrchestrationMetadata
}
export type AgnetCreateDeploymentBody = {
orchestration_plan: AgnetOrchestrationPlan
}
export type AgnetCreateDeploymentResult = {
deployment_id: string
status: string
agent_instances?: Array<{ instance_id?: string; role?: string; phase?: string }>
}
export type AgnetDeployment = {
deployment_id: string
status: string
@@ -21,21 +83,14 @@ export type AgnetDeployment = {
created_at: string
updated_at: string
orchestration_plan: {
intent_id?: string
template_hint?: string
objective?: string
agents?: Array<{
role_template?: string
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
project_id?: string
correlation_id?: string
}
risk_level?: string
budget?: AgnetBudget
agents?: AgnetAgentPlan[]
constraints?: AgnetConstraints
metadata?: AgnetOrchestrationMetadata
}
}
@@ -48,6 +103,24 @@ export async function listAgnetDeployments(): Promise<AgnetDeployment[]> {
return res.data?.data?.items ?? []
}
export async function createAgnetDeployment(
body: AgnetCreateDeploymentBody
): Promise<AgnetCreateDeploymentResult> {
const res = await api.post<ApiEnvelope<AgnetCreateDeploymentResult>>(
'/api/agnet/deployments',
body
)
const env = res.data
if (!env?.success) {
throw new Error(env?.message || 'Deployment request failed')
}
const data = env.data
if (!data?.deployment_id) {
throw new Error(env?.message || 'Invalid deployment response')
}
return data
}
export async function getAgnetDeploymentEvents(deploymentId: string) {
const res = await api.get<ApiEnvelope<{ items?: Array<Record<string, unknown>> }>>(
`/api/agnet/deployments/${deploymentId}/events`
@@ -0,0 +1,626 @@
import { useEffect, useMemo, useState } from 'react'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { Loader2, Plus, Trash2 } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Checkbox } from '@/components/ui/checkbox'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import {
Sheet,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
} from '@/components/ui/sheet'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { Textarea } from '@/components/ui/textarea'
import {
createAgnetDeployment,
type AgnetAgentPlan,
type AgnetOrchestrationPlan,
type AgnetSKSource,
} from './api'
type AgentFormRow = {
role_template: string
goal: string
default_model_id: string
sk_sources_json: string
profile_id: string
cloud_principals: string
network_policy_ref: string
policy_ref: string
deny_skill_ids: string
inherit_defaults: boolean
}
function splitComma(s: string): string[] {
return s
.split(',')
.map((x) => x.trim())
.filter(Boolean)
}
function buildAgent(row: AgentFormRow): AgnetAgentPlan {
let sk_sources: AgnetSKSource[] | undefined
const raw = row.sk_sources_json.trim()
if (raw) {
const parsed = JSON.parse(raw) as unknown
if (!Array.isArray(parsed)) {
throw new Error('sk_sources_must_be_array')
}
sk_sources = parsed as AgnetSKSource[]
}
const profileId = row.profile_id.trim()
const net = row.network_policy_ref.trim()
const clouds = splitComma(row.cloud_principals)
const hasRuntime =
profileId !== '' || net !== '' || clouds.length > 0
if (hasRuntime && profileId === '') {
throw new Error('profile_required_for_runtime')
}
const deny = splitComma(row.deny_skill_ids)
const pref = row.policy_ref.trim()
const runtime =
profileId || net || clouds.length > 0
? {
profile_id: profileId,
cloud_principal_refs: clouds.length > 0 ? clouds : undefined,
network_policy_ref: net || undefined,
}
: undefined
const skPolicy =
pref || deny.length > 0 || row.inherit_defaults
? {
policy_ref: pref || undefined,
deny_skill_ids: deny.length > 0 ? deny : undefined,
inherit_deployment_defaults: row.inherit_defaults,
}
: undefined
return {
role_template: row.role_template.trim(),
goal: row.goal.trim(),
default_model_id: row.default_model_id.trim() || undefined,
sk_sources,
runtime_execution: runtime,
sk_access_policy: skPolicy,
}
}
const DEFAULT_SK_JSON = `[
{
"type": "git",
"repo_ref": {
"connection_id": "",
"repo_url": "",
"ref": "main",
"paths": ["policy/sk.md"]
}
}
]`
const emptyAgent = (): AgentFormRow => ({
role_template: '',
goal: '',
default_model_id: '',
sk_sources_json: '[]',
profile_id: '',
cloud_principals: '',
network_policy_ref: '',
policy_ref: '',
deny_skill_ids: '',
inherit_defaults: true,
})
export function CreateAgnetDeploymentSheet({
open,
onOpenChange,
}: {
open: boolean
onOpenChange: (open: boolean) => void
}) {
const { t } = useTranslation()
const queryClient = useQueryClient()
const [templateHint, setTemplateHint] = useState('agile_min')
const [objective, setObjective] = useState('')
const [riskLevel, setRiskLevel] = useState<'low' | 'medium' | 'high'>(
'medium'
)
const [maxTokens, setMaxTokens] = useState(120_000)
const [maxCost, setMaxCost] = useState(8)
const [maxDurationSec, setMaxDurationSec] = useState(3600)
const [tenantId, setTenantId] = useState('ten_local')
const [projectId, setProjectId] = useState('prj_default')
const [correlationId, setCorrelationId] = useState('')
const [allowedModels, setAllowedModels] = useState('')
const [agents, setAgents] = useState<AgentFormRow[]>([emptyAgent()])
useEffect(() => {
if (open && !correlationId) {
setCorrelationId(crypto.randomUUID())
}
}, [open, correlationId])
const resetForm = () => {
setTemplateHint('agile_min')
setObjective('')
setRiskLevel('medium')
setMaxTokens(120_000)
setMaxCost(8)
setMaxDurationSec(3600)
setTenantId('ten_local')
setProjectId('prj_default')
setCorrelationId(crypto.randomUUID())
setAllowedModels('')
setAgents([emptyAgent()])
}
const mutation = useMutation({
mutationFn: async () => {
const intentId = crypto.randomUUID()
const allowed = splitComma(allowedModels)
let agentPlans: AgnetAgentPlan[]
try {
agentPlans = agents.map((row) => buildAgent(row))
} catch (e) {
if (e instanceof SyntaxError) {
throw new Error('invalid_sk_sources_json')
}
throw e
}
const plan: AgnetOrchestrationPlan = {
intent_id: intentId,
template_hint: templateHint.trim(),
objective: objective.trim(),
risk_level: riskLevel,
budget: {
max_tokens: maxTokens,
max_cost_usd: maxCost,
max_duration_sec: maxDurationSec,
},
agents: agentPlans,
constraints: {
allowed_model_ids: allowed.length > 0 ? allowed : undefined,
},
metadata: {
tenant_id: tenantId.trim(),
project_id: projectId.trim(),
correlation_id: correlationId.trim(),
},
}
return createAgnetDeployment({ orchestration_plan: plan })
},
onSuccess: (data) => {
toast.success(
t('Agnet deployment created', {
deployment_id: data.deployment_id,
}) as string
)
void queryClient.invalidateQueries({ queryKey: ['agnet', 'deployments'] })
onOpenChange(false)
resetForm()
},
onError: (err: Error) => {
const code = err.message
if (code === 'sk_sources_must_be_array') {
toast.error(t('Invalid SK sources JSON'))
return
}
if (code === 'invalid_sk_sources_json') {
toast.error(t('Invalid SK sources JSON'))
return
}
if (code === 'profile_required_for_runtime') {
toast.error(t('Execution profile required when cloud or network set'))
return
}
toast.error(err.message || t('Deployment request failed'))
},
})
const canSubmit = useMemo(() => {
return (
templateHint.trim() &&
objective.trim() &&
tenantId.trim() &&
projectId.trim() &&
correlationId.trim() &&
agents.every((a) => a.role_template.trim() && a.goal.trim())
)
}, [
templateHint,
objective,
tenantId,
projectId,
correlationId,
agents,
])
const fillExampleSk = (index: number) => {
setAgents((prev) => {
const next = [...prev]
next[index] = { ...next[index], sk_sources_json: DEFAULT_SK_JSON }
return next
})
}
return (
<Sheet open={open} onOpenChange={onOpenChange}>
<SheetContent className='flex max-h-svh flex-col gap-0 overflow-hidden sm:max-w-xl'>
<SheetHeader className='shrink-0 border-b pb-4'>
<SheetTitle>{t('Create Agnet deployment')}</SheetTitle>
<SheetDescription>
{t('Create Agnet deployment description')}
</SheetDescription>
</SheetHeader>
<div className='flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto py-4'>
<section className='space-y-3'>
<p className='text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground'>
{t('Deployment plan')}
</p>
<div className='grid gap-2'>
<Label>{t('Template hint')}</Label>
<Input
value={templateHint}
onChange={(e) => setTemplateHint(e.target.value)}
className='font-mono text-xs'
/>
</div>
<div className='grid gap-2'>
<Label>{t('Objective')}</Label>
<Textarea
value={objective}
onChange={(e) => setObjective(e.target.value)}
rows={2}
className='text-sm'
/>
</div>
<div className='grid gap-2'>
<Label>{t('Risk level')}</Label>
<Select
value={riskLevel}
onValueChange={(v) =>
setRiskLevel(v as 'low' | 'medium' | 'high')
}
>
<SelectTrigger className='h-9'>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value='low'>low</SelectItem>
<SelectItem value='medium'>medium</SelectItem>
<SelectItem value='high'>high</SelectItem>
</SelectContent>
</Select>
</div>
</section>
<section className='space-y-3'>
<p className='text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground'>
{t('Budget caps')}
</p>
<div className='grid grid-cols-3 gap-2'>
<div>
<Label className='text-xs'>{t('Max tokens')}</Label>
<Input
type='number'
value={maxTokens}
onChange={(e) => setMaxTokens(Number(e.target.value))}
className='mt-1 h-9 text-xs'
/>
</div>
<div>
<Label className='text-xs'>{t('Max cost USD')}</Label>
<Input
type='number'
step={0.01}
value={maxCost}
onChange={(e) => setMaxCost(Number(e.target.value))}
className='mt-1 h-9 text-xs'
/>
</div>
<div>
<Label className='text-xs'>{t('Max duration sec')}</Label>
<Input
type='number'
value={maxDurationSec}
onChange={(e) => setMaxDurationSec(Number(e.target.value))}
className='mt-1 h-9 text-xs'
/>
</div>
</div>
</section>
<section className='space-y-3'>
<p className='text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground'>
{t('Metadata')}
</p>
<div className='grid gap-2 sm:grid-cols-2'>
<div>
<Label className='text-xs'>tenant_id</Label>
<Input
value={tenantId}
onChange={(e) => setTenantId(e.target.value)}
className='mt-1 font-mono text-xs'
/>
</div>
<div>
<Label className='text-xs'>project_id</Label>
<Input
value={projectId}
onChange={(e) => setProjectId(e.target.value)}
className='mt-1 font-mono text-xs'
/>
</div>
</div>
<div className='grid gap-2'>
<Label className='text-xs'>correlation_id</Label>
<Input
value={correlationId}
onChange={(e) => setCorrelationId(e.target.value)}
className='font-mono text-xs'
/>
</div>
<div className='grid gap-2'>
<Label className='text-xs'>{t('Allowed models comma')}</Label>
<Input
value={allowedModels}
onChange={(e) => setAllowedModels(e.target.value)}
placeholder='mdl_claude_sonnet, mdl_claude_haiku'
className='font-mono text-xs'
/>
</div>
</section>
<section className='space-y-3'>
<div className='flex items-center justify-between'>
<p className='text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground'>
{t('Agents')}
</p>
<Button
type='button'
variant='outline'
size='sm'
className='h-8 gap-1 text-xs'
onClick={() => setAgents((a) => [...a, emptyAgent()])}
>
<Plus className='h-3.5 w-3.5' />
{t('Add agent')}
</Button>
</div>
{agents.map((agent, index) => (
<div
key={index}
className='rounded-xl border border-[color-mix(in_oklch,var(--primary)_18%,var(--border))] bg-[color-mix(in_oklch,var(--card)_55%,transparent)] p-3'
>
<div className='mb-2 flex items-center justify-between'>
<span className='text-xs font-medium'>
{t('Agent')} #{index + 1}
</span>
{agents.length > 1 ? (
<Button
type='button'
variant='ghost'
size='icon'
className='h-8 w-8'
onClick={() =>
setAgents((prev) => prev.filter((_, i) => i !== index))
}
>
<Trash2 className='h-4 w-4 text-destructive' />
</Button>
) : null}
</div>
<div className='grid gap-2'>
<Input
placeholder={t('Role template')}
value={agent.role_template}
onChange={(e) =>
setAgents((prev) => {
const n = [...prev]
n[index] = {
...n[index],
role_template: e.target.value,
}
return n
})
}
className='font-mono text-xs'
/>
<Textarea
placeholder={t('Goal')}
value={agent.goal}
onChange={(e) =>
setAgents((prev) => {
const n = [...prev]
n[index] = { ...n[index], goal: e.target.value }
return n
})
}
rows={2}
className='text-sm'
/>
<Input
placeholder={t('Default model id')}
value={agent.default_model_id}
onChange={(e) =>
setAgents((prev) => {
const n = [...prev]
n[index] = {
...n[index],
default_model_id: e.target.value,
}
return n
})
}
className='font-mono text-xs'
/>
<div className='flex items-center justify-between pt-1'>
<Label className='text-xs'>{t('SK sources JSON')}</Label>
<Button
type='button'
variant='ghost'
size='sm'
className='h-7 text-[11px]'
onClick={() => fillExampleSk(index)}
>
{t('Insert git SK example')}
</Button>
</div>
<Textarea
value={agent.sk_sources_json}
onChange={(e) =>
setAgents((prev) => {
const n = [...prev]
n[index] = {
...n[index],
sk_sources_json: e.target.value,
}
return n
})
}
spellCheck={false}
className='min-h-[100px] font-mono text-[11px]'
/>
<p className='text-[11px] font-semibold text-muted-foreground'>
{t('Runtime binding')}
</p>
<Input
placeholder={t('Execution profile')}
value={agent.profile_id}
onChange={(e) =>
setAgents((prev) => {
const n = [...prev]
n[index] = { ...n[index], profile_id: e.target.value }
return n
})
}
className='font-mono text-xs'
/>
<Input
placeholder={t('Cloud principals comma')}
value={agent.cloud_principals}
onChange={(e) =>
setAgents((prev) => {
const n = [...prev]
n[index] = {
...n[index],
cloud_principals: e.target.value,
}
return n
})
}
className='font-mono text-xs'
/>
<Input
placeholder={t('Network policy')}
value={agent.network_policy_ref}
onChange={(e) =>
setAgents((prev) => {
const n = [...prev]
n[index] = {
...n[index],
network_policy_ref: e.target.value,
}
return n
})
}
className='font-mono text-xs'
/>
<p className='text-[11px] font-semibold text-muted-foreground'>
{t('SK access policy')}
</p>
<Input
placeholder={t('Policy ref')}
value={agent.policy_ref}
onChange={(e) =>
setAgents((prev) => {
const n = [...prev]
n[index] = { ...n[index], policy_ref: e.target.value }
return n
})
}
className='font-mono text-xs'
/>
<Input
placeholder={t('Denied skills')}
value={agent.deny_skill_ids}
onChange={(e) =>
setAgents((prev) => {
const n = [...prev]
n[index] = {
...n[index],
deny_skill_ids: e.target.value,
}
return n
})
}
className='font-mono text-xs'
/>
<label className='flex items-center gap-2 text-xs'>
<Checkbox
checked={agent.inherit_defaults}
onCheckedChange={(v) =>
setAgents((prev) => {
const n = [...prev]
n[index] = {
...n[index],
inherit_defaults: Boolean(v),
}
return n
})
}
/>
{t('Inherits deployment defaults')}
</label>
</div>
</div>
))}
</section>
</div>
<SheetFooter className='shrink-0 border-t pt-4'>
<Button
type='button'
variant='outline'
onClick={() => onOpenChange(false)}
>
{t('Cancel')}
</Button>
<Button
type='button'
disabled={!canSubmit || mutation.isPending}
onClick={() => mutation.mutate()}
>
{mutation.isPending ? (
<Loader2 className='mr-2 h-4 w-4 animate-spin' />
) : null}
{t('Submit deployment')}
</Button>
</SheetFooter>
</SheetContent>
</Sheet>
)
}
@@ -17,6 +17,7 @@ import {
GitCommit,
Hash,
PlayCircle,
Plus,
Rocket,
Search,
ShieldCheck,
@@ -45,6 +46,7 @@ import {
type AgnetRuntimeExecution,
type AgnetSKAccessPolicy,
} from './api'
import { CreateAgnetDeploymentSheet } from './create-agnet-deployment-sheet'
type StatusKey = 'running' | 'success' | 'failed' | 'pending'
@@ -201,6 +203,7 @@ export function AgnetDeploymentsPage() {
const { t } = useTranslation()
const [filter, setFilter] = useState<'all' | StatusKey>('all')
const [keyword, setKeyword] = useState('')
const [createOpen, setCreateOpen] = useState(false)
const { data = [], isLoading } = useQuery({
queryKey: ['agnet', 'deployments'],
@@ -225,6 +228,7 @@ export function AgnetDeploymentsPage() {
}, [data, filter, keyword])
return (
<>
<PageSurface
title={t('Deployments')}
subtitle={t(
@@ -232,6 +236,15 @@ export function AgnetDeploymentsPage() {
)}
toolbar={
<>
<Button
type='button'
size='sm'
className='h-9 gap-1.5 rounded-xl'
onClick={() => setCreateOpen(true)}
>
<Plus className='h-3.5 w-3.5' />
{t('New deployment')}
</Button>
<div className='relative'>
<Search className='pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground' />
<Input
@@ -332,6 +345,8 @@ export function AgnetDeploymentsPage() {
</div>
)}
</PageSurface>
<CreateAgnetDeploymentSheet open={createOpen} onOpenChange={setCreateOpen} />
</>
)
}
+26 -2
View File
@@ -209,6 +209,30 @@
"Policy ref": "Policy ref",
"Denied skills": "Denied skills",
"Inherits deployment defaults": "Inherits deployment defaults",
"Add agent": "Add agent",
"Agent": "Agent",
"Agnet deployment created": "Agnet deployment created ({{deployment_id}})",
"Allowed models comma": "Allowed model IDs (comma-separated)",
"Budget caps": "Budget caps",
"Cloud principals comma": "Cloud principals (comma-separated)",
"Create Agnet deployment": "Create Agnet deployment",
"Create Agnet deployment description": "Send the full orchestration plan in one request. Manager stores it; execution runs on Agnet.",
"Default model id": "Default model ID",
"Deployment plan": "Deployment plan",
"Deployment request failed": "Deployment request failed",
"Execution profile required when cloud or network set": "Set an execution profile when you configure cloud principals or a network policy.",
"Goal": "Goal",
"Insert git SK example": "Insert Git SK example",
"Invalid SK sources JSON": "SK sources must be a valid JSON array",
"Max cost USD": "Max cost (USD)",
"Max duration sec": "Max duration (seconds)",
"Max tokens": "Max tokens",
"Objective": "Objective",
"Risk level": "Risk level",
"Role template": "Role template",
"SK sources JSON": "SK sources (JSON)",
"Submit deployment": "Submit deployment",
"Template hint": "Template hint",
"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",
@@ -1613,8 +1637,8 @@
"Get Started": "Get Started",
"Git binding": "Git binding",
"Git sources": "Git sources",
"Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Allocate cloud permissions for sub-agents (VMs, roles, scopes) in the same plan; deploy sub-agents after bindings exist so each run inherits allow/deny SK policy. Below lists immutable snapshot anchors (Git commit / upload artifact) resolved for auditing.",
"Git sources subtitle": "Bind Git code and SK tool repositories, attach cloud permissions for sub-agents, deploy, then review snapshot anchors per deployment.",
"Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agnet when it starts each deployment; effective permissions are stored and enforced on Agnet. Below lists immutable snapshot anchors (Git commit / upload artifact) Agnet resolved for auditing.",
"Git sources subtitle": "Configure bindings and deployment parameters for Agnet, then review snapshot anchors per deployment (enforcement lives on Agnet).",
"Git sources workflow title": "Typical setup flow",
"Git sources workflow step 1": "Bind your team’s Git repositories that hold application code and delivery context.",
"Git sources workflow step 2": "Bind SK tool repositories (skills registry) your tenant is allowed to draw from.",
+26 -2
View File
@@ -209,6 +209,30 @@
"Policy ref": "Policy ref",
"Denied skills": "Denied skills",
"Inherits deployment defaults": "Inherits deployment defaults",
"Add agent": "Add agent",
"Agent": "Agent",
"Agnet deployment created": "Agnet deployment created ({{deployment_id}})",
"Allowed models comma": "Allowed model IDs (comma-separated)",
"Budget caps": "Budget caps",
"Cloud principals comma": "Cloud principals (comma-separated)",
"Create Agnet deployment": "Create Agnet deployment",
"Create Agnet deployment description": "Send the full orchestration plan in one request. Manager stores it; execution runs on Agnet.",
"Default model id": "Default model ID",
"Deployment plan": "Deployment plan",
"Deployment request failed": "Deployment request failed",
"Execution profile required when cloud or network set": "Set an execution profile when you configure cloud principals or a network policy.",
"Goal": "Goal",
"Insert git SK example": "Insert Git SK example",
"Invalid SK sources JSON": "SK sources must be a valid JSON array",
"Max cost USD": "Max cost (USD)",
"Max duration sec": "Max duration (seconds)",
"Max tokens": "Max tokens",
"Objective": "Objective",
"Risk level": "Risk level",
"Role template": "Role template",
"SK sources JSON": "SK sources (JSON)",
"Submit deployment": "Submit deployment",
"Template hint": "Template hint",
"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",
@@ -1613,8 +1637,8 @@
"Get Started": "Commencer",
"Git binding": "Git binding",
"Git sources": "Git sources",
"Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Allocate cloud permissions for sub-agents (VMs, roles, scopes) in the same plan; deploy sub-agents after bindings exist so each run inherits allow/deny SK policy. Below lists immutable snapshot anchors (Git commit / upload artifact) resolved for auditing.",
"Git sources subtitle": "Bind Git code and SK tool repositories, attach cloud permissions for sub-agents, deploy, then review snapshot anchors per deployment.",
"Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agnet when it starts each deployment; effective permissions are stored and enforced on Agnet. Below lists immutable snapshot anchors (Git commit / upload artifact) Agnet resolved for auditing.",
"Git sources subtitle": "Configure bindings and deployment parameters for Agnet, then review snapshot anchors per deployment (enforcement lives on Agnet).",
"Git sources workflow title": "Typical setup flow",
"Git sources workflow step 1": "Bind your team’s Git repositories that hold application code and delivery context.",
"Git sources workflow step 2": "Bind SK tool repositories (skills registry) your tenant is allowed to draw from.",
+26 -2
View File
@@ -209,6 +209,30 @@
"Policy ref": "Policy ref",
"Denied skills": "Denied skills",
"Inherits deployment defaults": "Inherits deployment defaults",
"Add agent": "Add agent",
"Agent": "Agent",
"Agnet deployment created": "Agnet deployment created ({{deployment_id}})",
"Allowed models comma": "Allowed model IDs (comma-separated)",
"Budget caps": "Budget caps",
"Cloud principals comma": "Cloud principals (comma-separated)",
"Create Agnet deployment": "Create Agnet deployment",
"Create Agnet deployment description": "Send the full orchestration plan in one request. Manager stores it; execution runs on Agnet.",
"Default model id": "Default model ID",
"Deployment plan": "Deployment plan",
"Deployment request failed": "Deployment request failed",
"Execution profile required when cloud or network set": "Set an execution profile when you configure cloud principals or a network policy.",
"Goal": "Goal",
"Insert git SK example": "Insert Git SK example",
"Invalid SK sources JSON": "SK sources must be a valid JSON array",
"Max cost USD": "Max cost (USD)",
"Max duration sec": "Max duration (seconds)",
"Max tokens": "Max tokens",
"Objective": "Objective",
"Risk level": "Risk level",
"Role template": "Role template",
"SK sources JSON": "SK sources (JSON)",
"Submit deployment": "Submit deployment",
"Template hint": "Template hint",
"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",
@@ -1613,8 +1637,8 @@
"Get Started": "開始する",
"Git binding": "Git binding",
"Git sources": "Git sources",
"Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Allocate cloud permissions for sub-agents (VMs, roles, scopes) in the same plan; deploy sub-agents after bindings exist so each run inherits allow/deny SK policy. Below lists immutable snapshot anchors (Git commit / upload artifact) resolved for auditing.",
"Git sources subtitle": "Bind Git code and SK tool repositories, attach cloud permissions for sub-agents, deploy, then review snapshot anchors per deployment.",
"Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agnet when it starts each deployment; effective permissions are stored and enforced on Agnet. Below lists immutable snapshot anchors (Git commit / upload artifact) Agnet resolved for auditing.",
"Git sources subtitle": "Configure bindings and deployment parameters for Agnet, then review snapshot anchors per deployment (enforcement lives on Agnet).",
"Git sources workflow title": "Typical setup flow",
"Git sources workflow step 1": "Bind your team’s Git repositories that hold application code and delivery context.",
"Git sources workflow step 2": "Bind SK tool repositories (skills registry) your tenant is allowed to draw from.",
+26 -2
View File
@@ -209,6 +209,30 @@
"Policy ref": "Policy ref",
"Denied skills": "Denied skills",
"Inherits deployment defaults": "Inherits deployment defaults",
"Add agent": "Add agent",
"Agent": "Agent",
"Agnet deployment created": "Agnet deployment created ({{deployment_id}})",
"Allowed models comma": "Allowed model IDs (comma-separated)",
"Budget caps": "Budget caps",
"Cloud principals comma": "Cloud principals (comma-separated)",
"Create Agnet deployment": "Create Agnet deployment",
"Create Agnet deployment description": "Send the full orchestration plan in one request. Manager stores it; execution runs on Agnet.",
"Default model id": "Default model ID",
"Deployment plan": "Deployment plan",
"Deployment request failed": "Deployment request failed",
"Execution profile required when cloud or network set": "Set an execution profile when you configure cloud principals or a network policy.",
"Goal": "Goal",
"Insert git SK example": "Insert Git SK example",
"Invalid SK sources JSON": "SK sources must be a valid JSON array",
"Max cost USD": "Max cost (USD)",
"Max duration sec": "Max duration (seconds)",
"Max tokens": "Max tokens",
"Objective": "Objective",
"Risk level": "Risk level",
"Role template": "Role template",
"SK sources JSON": "SK sources (JSON)",
"Submit deployment": "Submit deployment",
"Template hint": "Template hint",
"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",
@@ -1613,8 +1637,8 @@
"Get Started": "Начать",
"Git binding": "Git binding",
"Git sources": "Git sources",
"Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Allocate cloud permissions for sub-agents (VMs, roles, scopes) in the same plan; deploy sub-agents after bindings exist so each run inherits allow/deny SK policy. Below lists immutable snapshot anchors (Git commit / upload artifact) resolved for auditing.",
"Git sources subtitle": "Bind Git code and SK tool repositories, attach cloud permissions for sub-agents, deploy, then review snapshot anchors per deployment.",
"Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agnet when it starts each deployment; effective permissions are stored and enforced on Agnet. Below lists immutable snapshot anchors (Git commit / upload artifact) Agnet resolved for auditing.",
"Git sources subtitle": "Configure bindings and deployment parameters for Agnet, then review snapshot anchors per deployment (enforcement lives on Agnet).",
"Git sources workflow title": "Typical setup flow",
"Git sources workflow step 1": "Bind your team’s Git repositories that hold application code and delivery context.",
"Git sources workflow step 2": "Bind SK tool repositories (skills registry) your tenant is allowed to draw from.",
+26 -2
View File
@@ -209,6 +209,30 @@
"Policy ref": "Policy ref",
"Denied skills": "Denied skills",
"Inherits deployment defaults": "Inherits deployment defaults",
"Add agent": "Add agent",
"Agent": "Agent",
"Agnet deployment created": "Agnet deployment created ({{deployment_id}})",
"Allowed models comma": "Allowed model IDs (comma-separated)",
"Budget caps": "Budget caps",
"Cloud principals comma": "Cloud principals (comma-separated)",
"Create Agnet deployment": "Create Agnet deployment",
"Create Agnet deployment description": "Send the full orchestration plan in one request. Manager stores it; execution runs on Agnet.",
"Default model id": "Default model ID",
"Deployment plan": "Deployment plan",
"Deployment request failed": "Deployment request failed",
"Execution profile required when cloud or network set": "Set an execution profile when you configure cloud principals or a network policy.",
"Goal": "Goal",
"Insert git SK example": "Insert Git SK example",
"Invalid SK sources JSON": "SK sources must be a valid JSON array",
"Max cost USD": "Max cost (USD)",
"Max duration sec": "Max duration (seconds)",
"Max tokens": "Max tokens",
"Objective": "Objective",
"Risk level": "Risk level",
"Role template": "Role template",
"SK sources JSON": "SK sources (JSON)",
"Submit deployment": "Submit deployment",
"Template hint": "Template hint",
"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",
@@ -1613,8 +1637,8 @@
"Get Started": "Bắt đầu",
"Git binding": "Git binding",
"Git sources": "Git sources",
"Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Allocate cloud permissions for sub-agents (VMs, roles, scopes) in the same plan; deploy sub-agents after bindings exist so each run inherits allow/deny SK policy. Below lists immutable snapshot anchors (Git commit / upload artifact) resolved for auditing.",
"Git sources subtitle": "Bind Git code and SK tool repositories, attach cloud permissions for sub-agents, deploy, then review snapshot anchors per deployment.",
"Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agnet when it starts each deployment; effective permissions are stored and enforced on Agnet. Below lists immutable snapshot anchors (Git commit / upload artifact) Agnet resolved for auditing.",
"Git sources subtitle": "Configure bindings and deployment parameters for Agnet, then review snapshot anchors per deployment (enforcement lives on Agnet).",
"Git sources workflow title": "Typical setup flow",
"Git sources workflow step 1": "Bind your team’s Git repositories that hold application code and delivery context.",
"Git sources workflow step 2": "Bind SK tool repositories (skills registry) your tenant is allowed to draw from.",
+26 -2
View File
@@ -209,6 +209,30 @@
"Policy ref": "策略引用",
"Denied skills": "禁止的技能",
"Inherits deployment defaults": "继承部署默认策略",
"Add agent": "添加 Agent",
"Agent": "Agent",
"Agnet deployment created": "Agnet 部署已创建({{deployment_id}})",
"Allowed models comma": "允许的模型 ID(逗号分隔)",
"Budget caps": "预算上限",
"Cloud principals comma": "云身份引用(逗号分隔)",
"Create Agnet deployment": "创建 Agnet 部署",
"Create Agnet deployment description": "在一次请求中提交完整编排计划。Manager 负责记录;实际执行在 Agnet。",
"Default model id": "默认模型 ID",
"Deployment plan": "编排计划",
"Deployment request failed": "部署请求失败",
"Execution profile required when cloud or network set": "填写云身份或网络策略时,请同时设置执行档案。",
"Goal": "Agent 目标",
"Insert git SK example": "插入 Git SK 示例",
"Invalid SK sources JSON": "SK 来源须为有效的 JSON 数组",
"Max cost USD": "最大费用(USD)",
"Max duration sec": "最长时长(秒)",
"Max tokens": "最大 token 数",
"Objective": "计划目标",
"Risk level": "风险等级",
"Role template": "角色模板",
"SK sources JSON": "SK 来源(JSON)",
"Submit deployment": "提交部署",
"Template hint": "模板提示",
"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": "敏捷最小编队",
@@ -1613,8 +1637,8 @@
"Get Started": "开始使用",
"Git binding": "Git 绑定",
"Git sources": "Git 来源",
"Git sources binding explainer": "Skill(SK)定义以 Git 为唯一事实源。Manager 不在此编辑 Markdown:请在 Heicode 客户端或部署计划的 sk_sources 中登记代码仓库、SK 工具仓库与引用;并在同一计划中为子 Agent 分配云上权限(虚拟机、角色、API 范围等)。完成绑定后再部署子 Agent,使每次运行继承允许的 SK 与被禁止的 SK 策略。下列为运行时解析得到的不可变快照锚点(Git commit / 上传制品),供审计追溯。",
"Git sources subtitle": "绑定代码与 SK 工具仓库、为子 Agent 分配云上权限并部署后,在此按部署查看快照锚点。",
"Git sources binding explainer": "Skill(SK)定义以 Git 为唯一事实源。Manager 不在此编辑 Markdown:请在 Heicode 客户端或部署计划的 sk_sources 中登记仓库与引用;运行时与 SK 策略等参数在 Agnet 拉起编队/子 Agent 时传入。有效权限与策略落账在 Agnet 侧并由其执行;Manager 仅展示 Agnet 回传的不可变快照锚点(Git commit / 上传制品)供审计。",
"Git sources subtitle": "在部署参数中把绑定与策略交给 Agnet,在此按部署查看快照锚点(执行权在 Agnet)。",
"Git sources workflow title": "典型配置顺序",
"Git sources workflow step 1": "绑定团队用于应用代码与交付上下文的 Git 仓库。",
"Git sources workflow step 2": "绑定 SK 工具仓库(技能来源),声明租户可引用的工具集。",