omx(team): auto-checkpoint worker-2 [2]
This commit is contained in:
@@ -6,14 +6,25 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/heicode/manager/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/heicode/manager/common"
|
||||
)
|
||||
|
||||
const (
|
||||
agnetRiskLow = "low"
|
||||
agnetRiskMedium = "medium"
|
||||
agnetRiskHigh = "high"
|
||||
|
||||
agnetResourceGit = "git"
|
||||
agnetResourceSK = "sk"
|
||||
agnetResourceProjectDoc = "project_doc"
|
||||
agnetResourceCloudAccount = "cloud_account"
|
||||
agnetResourceCloudResource = "cloud_resource"
|
||||
|
||||
agnetGrantStatusPending = "pending"
|
||||
agnetGrantStatusActive = "active"
|
||||
agnetGrantStatusDisabled = "disabled"
|
||||
agnetGrantStatusRevoked = "revoked"
|
||||
)
|
||||
|
||||
type agnetBudget struct {
|
||||
@@ -51,13 +62,32 @@ type agnetSKAccessPolicy struct {
|
||||
InheritDeploymentDefaults bool `json:"inherit_deployment_defaults"`
|
||||
}
|
||||
|
||||
// agnetResourceGrant is the Manager-side resource binding envelope from docs/heicode.md P1.
|
||||
// It deliberately carries only metadata, scoped permissions and secret_ref, never plaintext secrets.
|
||||
type agnetResourceGrant struct {
|
||||
GrantID string `json:"grant_id"`
|
||||
ResourceID string `json:"resource_id"`
|
||||
ResourceType string `json:"resource_type"`
|
||||
TenantID string `json:"tenant_id"`
|
||||
ProjectID string `json:"project_id"`
|
||||
TargetRole string `json:"target_role"`
|
||||
TargetAgentRef string `json:"target_agent_ref"`
|
||||
PermissionScope []string `json:"permission_scope"`
|
||||
Constraints map[string]string `json:"constraints"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
Status string `json:"status"`
|
||||
SecretRef string `json:"secret_ref"`
|
||||
Audit map[string]string `json:"audit"`
|
||||
}
|
||||
|
||||
type agnetAgentPlan struct {
|
||||
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"`
|
||||
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"`
|
||||
ResourceGrants []agnetResourceGrant `json:"resource_grants"`
|
||||
}
|
||||
|
||||
type agnetConstraints struct {
|
||||
@@ -158,6 +188,30 @@ func containsString(values []string, target string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func containsSensitiveGrantField(values map[string]string) bool {
|
||||
for key := range values {
|
||||
normalized := strings.ToLower(strings.ReplaceAll(strings.TrimSpace(key), "-", "_"))
|
||||
if strings.Contains(normalized, "password") ||
|
||||
strings.Contains(normalized, "token") ||
|
||||
strings.Contains(normalized, "secret") ||
|
||||
strings.Contains(normalized, "private_key") ||
|
||||
strings.Contains(normalized, "access_key") ||
|
||||
strings.Contains(normalized, "credential") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func agnetResourceTypeNeedsSecretRef(resourceType string) bool {
|
||||
switch resourceType {
|
||||
case agnetResourceGit, agnetResourceSK, agnetResourceCloudAccount, agnetResourceCloudResource:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func agnetRuntimePartiallySet(r agnetRuntimeExecution) bool {
|
||||
return strings.TrimSpace(r.ProfileID) != "" ||
|
||||
len(r.CloudPrincipalRefs) > 0 ||
|
||||
@@ -235,7 +289,50 @@ func validateAgentSKAccessPolicy(c *gin.Context, agent agnetAgentPlan) bool {
|
||||
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")
|
||||
agnetError(c, "SK_POLICY_REJECTED", "sk_access_policy.policy_ref or inherit_deployment_defaults is required when deny_skill_ids set")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func validateResourceGrant(c *gin.Context, plan agnetOrchestrationPlan, agent agnetAgentPlan, grant agnetResourceGrant) bool {
|
||||
resourceType := strings.TrimSpace(grant.ResourceType)
|
||||
switch resourceType {
|
||||
case agnetResourceGit, agnetResourceSK, agnetResourceProjectDoc, agnetResourceCloudAccount, agnetResourceCloudResource:
|
||||
default:
|
||||
agnetError(c, "RESOURCE_GRANT_INVALID", "resource_grants.resource_type must be git/sk/project_doc/cloud_account/cloud_resource")
|
||||
return false
|
||||
}
|
||||
|
||||
if strings.TrimSpace(grant.GrantID) == "" || strings.TrimSpace(grant.ResourceID) == "" ||
|
||||
strings.TrimSpace(grant.TargetRole) == "" || strings.TrimSpace(grant.TargetAgentRef) == "" {
|
||||
agnetError(c, "RESOURCE_GRANT_INVALID", "resource_grants require grant_id/resource_id/target_role/target_agent_ref")
|
||||
return false
|
||||
}
|
||||
if strings.TrimSpace(grant.TenantID) != plan.Metadata.TenantID || strings.TrimSpace(grant.ProjectID) != plan.Metadata.ProjectID {
|
||||
agnetError(c, "RESOURCE_GRANT_INVALID", "resource_grants tenant_id/project_id must match orchestration metadata")
|
||||
return false
|
||||
}
|
||||
if strings.TrimSpace(grant.TargetRole) != strings.TrimSpace(agent.RoleTemplate) {
|
||||
agnetError(c, "RESOURCE_GRANT_INVALID", "resource_grants.target_role must match the assigned agent role")
|
||||
return false
|
||||
}
|
||||
if len(grant.PermissionScope) == 0 {
|
||||
agnetError(c, "RESOURCE_GRANT_INVALID", "resource_grants.permission_scope must not be empty")
|
||||
return false
|
||||
}
|
||||
switch strings.TrimSpace(grant.Status) {
|
||||
case agnetGrantStatusPending, agnetGrantStatusActive, agnetGrantStatusDisabled, agnetGrantStatusRevoked:
|
||||
default:
|
||||
agnetError(c, "RESOURCE_GRANT_INVALID", "resource_grants.status must be pending/active/disabled/revoked")
|
||||
return false
|
||||
}
|
||||
if agnetResourceTypeNeedsSecretRef(resourceType) && strings.TrimSpace(grant.SecretRef) == "" {
|
||||
agnetError(c, "RESOURCE_GRANT_SECRET_REF_REQUIRED", "resource_grants.secret_ref is required for credential-backed resources")
|
||||
return false
|
||||
}
|
||||
if containsSensitiveGrantField(grant.Metadata) || containsSensitiveGrantField(grant.Constraints) || containsSensitiveGrantField(grant.Audit) {
|
||||
agnetError(c, "RESOURCE_GRANT_SECRET_REJECTED", "resource_grants metadata/constraints/audit must not contain plaintext credential fields")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -300,6 +397,11 @@ func validateOrchestrationPlan(c *gin.Context, plan agnetOrchestrationPlan) bool
|
||||
if !validateAgentSKAccessPolicy(c, agent) {
|
||||
return false
|
||||
}
|
||||
for _, grant := range agent.ResourceGrants {
|
||||
if !validateResourceGrant(c, plan, agent, grant) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user