feat: improved codex and auggie default permissions
This commit is contained in:
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/kandev/kandev/internal/agent/discovery"
|
||||
"github.com/kandev/kandev/internal/agent/registry"
|
||||
agentsettingscontroller "github.com/kandev/kandev/internal/agent/settings/controller"
|
||||
"github.com/kandev/kandev/internal/common/config"
|
||||
"github.com/kandev/kandev/internal/common/logger"
|
||||
@@ -17,7 +18,9 @@ func provideServices(cfg *config.Config, log *logger.Logger, repos *Repositories
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
agentSettingsController := agentsettingscontroller.NewController(repos.AgentSettings, discoveryRegistry, repos.Task, log)
|
||||
agentRegistry := registry.NewRegistry(log)
|
||||
agentRegistry.LoadDefaults()
|
||||
agentSettingsController := agentsettingscontroller.NewController(repos.AgentSettings, discoveryRegistry, agentRegistry, repos.Task, log)
|
||||
|
||||
userSvc := userservice.NewService(repos.User, eventBus, log)
|
||||
editorSvc := editorservice.NewService(repos.Editor, repos.Task, userSvc)
|
||||
|
||||
@@ -88,7 +88,7 @@ func LoadRegistry() (*Registry, error) {
|
||||
for _, agentCfg := range agentConfigs {
|
||||
// Convert registry.AgentTypeConfig to discovery.KnownAgent
|
||||
knownAgent := KnownAgent{
|
||||
Name: extractAgentName(agentCfg.ID),
|
||||
Name: agentCfg.ID, // Use ID directly as the short name (e.g., "auggie", "codex")
|
||||
DisplayName: agentCfg.DisplayName,
|
||||
SupportsMCP: agentCfg.Discovery.SupportsMCP,
|
||||
MCPConfigPath: agentCfg.Discovery.MCPConfigPath,
|
||||
@@ -106,12 +106,6 @@ func LoadRegistry() (*Registry, error) {
|
||||
return &Registry{adapters: adapters, definitions: definitions}, nil
|
||||
}
|
||||
|
||||
// extractAgentName extracts the short name from an agent ID (e.g., "auggie-agent" -> "auggie")
|
||||
func extractAgentName(id string) string {
|
||||
name := strings.TrimSuffix(id, "-agent")
|
||||
return name
|
||||
}
|
||||
|
||||
func (r *Registry) Definitions() []KnownAgent {
|
||||
if r == nil {
|
||||
return nil
|
||||
|
||||
@@ -22,6 +22,11 @@ type CommandOptions struct {
|
||||
Model string // Model to use (appended via ModelFlag if set)
|
||||
SessionID string // Session ID to resume (appended via SessionConfig.ResumeFlag if not ResumeViaACP)
|
||||
AutoApprove bool // If true, skip permission flags (auto-approve all tool calls)
|
||||
|
||||
// PermissionValues contains the values for each permission setting
|
||||
// Keys are setting names (e.g., "allow_indexing", "auto_approve")
|
||||
// Values are the boolean state of each setting
|
||||
PermissionValues map[string]bool
|
||||
}
|
||||
|
||||
// BuildCommand builds a command slice from agent config and options
|
||||
@@ -58,6 +63,34 @@ func (cb *CommandBuilder) BuildCommand(agentConfig *registry.AgentTypeConfig, op
|
||||
}
|
||||
}
|
||||
|
||||
// Apply permission settings that use CLI flags
|
||||
// Each permission setting can define its own CLI flag via ApplyMethod="cli_flag"
|
||||
if permSettings := agentConfig.PermissionSettings; permSettings != nil {
|
||||
for settingName, setting := range permSettings {
|
||||
// Skip if not supported or not a CLI flag setting
|
||||
if !setting.Supported || setting.ApplyMethod != "cli_flag" || setting.CLIFlag == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the value for this setting from options
|
||||
value, exists := opts.PermissionValues[settingName]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
// Only apply if the setting value is true
|
||||
if value {
|
||||
if setting.CLIFlagValue != "" {
|
||||
// Flag with value: "--flag value"
|
||||
cmd = append(cmd, setting.CLIFlag, setting.CLIFlagValue)
|
||||
} else {
|
||||
// Boolean flag: "--flag"
|
||||
cmd = append(cmd, setting.CLIFlag)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -180,8 +180,16 @@ func (cm *ContainerManager) buildContainerConfig(config ContainerConfig) (docker
|
||||
|
||||
// Build command using CommandBuilder
|
||||
cmdOpts := CommandOptions{
|
||||
Model: config.Model,
|
||||
SessionID: config.SessionID,
|
||||
Model: config.Model,
|
||||
SessionID: config.SessionID,
|
||||
PermissionValues: make(map[string]bool),
|
||||
}
|
||||
// Get profile settings if available
|
||||
if config.ProfileInfo != nil {
|
||||
cmdOpts.AutoApprove = config.ProfileInfo.AutoApprove
|
||||
cmdOpts.PermissionValues["auto_approve"] = config.ProfileInfo.AutoApprove
|
||||
cmdOpts.PermissionValues["allow_indexing"] = config.ProfileInfo.AllowIndexing
|
||||
cmdOpts.PermissionValues["dangerously_skip_permissions"] = config.ProfileInfo.DangerouslySkipPermissions
|
||||
}
|
||||
cmd := cm.commandBuilder.BuildCommand(agentConfig, cmdOpts)
|
||||
|
||||
|
||||
@@ -228,9 +228,9 @@ func (m *Manager) createExecution(ctx context.Context, taskID string, info *Work
|
||||
|
||||
executionID := uuid.New().String()
|
||||
|
||||
agentConfig, err := m.registry.Get(info.AgentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("agent type %q not found in registry: %w", info.AgentID, err)
|
||||
agentConfig, ok := m.registry.Get(info.AgentID)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("agent type %q not found in registry", info.AgentID)
|
||||
}
|
||||
|
||||
req := &RuntimeCreateRequest{
|
||||
@@ -387,8 +387,7 @@ func (m *Manager) Launch(ctx context.Context, req *LaunchRequest) (*AgentExecuti
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to resolve agent profile: %w", err)
|
||||
}
|
||||
// Map agent name to registry ID (e.g., "auggie" -> "auggie-agent")
|
||||
agentTypeName = profileInfo.AgentName + "-agent"
|
||||
agentTypeName = profileInfo.AgentName
|
||||
m.logger.Info("resolved agent profile",
|
||||
zap.String("profile_id", req.AgentProfileID),
|
||||
zap.String("agent_name", profileInfo.AgentName),
|
||||
@@ -401,9 +400,9 @@ func (m *Manager) Launch(ctx context.Context, req *LaunchRequest) (*AgentExecuti
|
||||
}
|
||||
|
||||
// 2. Get agent config from registry
|
||||
agentConfig, err := m.registry.Get(agentTypeName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("agent type %q not found in registry: %w", agentTypeName, err)
|
||||
agentConfig, ok := m.registry.Get(agentTypeName)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("agent type %q not found in registry", agentTypeName)
|
||||
}
|
||||
|
||||
if !agentConfig.Enabled {
|
||||
@@ -475,9 +474,6 @@ func (m *Manager) Launch(ctx context.Context, req *LaunchRequest) (*AgentExecuti
|
||||
if profileInfo.AutoApprove {
|
||||
reqWithWorktree.Env["AGENTCTL_AUTO_APPROVE_PERMISSIONS"] = "true"
|
||||
}
|
||||
if profileInfo.DangerouslySkipPermissions {
|
||||
reqWithWorktree.Env["AGENT_DANGEROUSLY_SKIP_PERMISSIONS"] = "true"
|
||||
}
|
||||
if profileInfo.Plan != "" {
|
||||
reqWithWorktree.Env["AGENT_PLAN"] = profileInfo.Plan
|
||||
}
|
||||
@@ -544,18 +540,24 @@ func (m *Manager) Launch(ctx context.Context, req *LaunchRequest) (*AgentExecuti
|
||||
// Build agent command string for later use with StartAgentProcess
|
||||
model := ""
|
||||
autoApprove := false
|
||||
permissionValues := make(map[string]bool)
|
||||
if profileInfo != nil {
|
||||
model = profileInfo.Model
|
||||
autoApprove = profileInfo.AutoApprove
|
||||
// Build permission values map from profile info
|
||||
permissionValues["auto_approve"] = profileInfo.AutoApprove
|
||||
permissionValues["allow_indexing"] = profileInfo.AllowIndexing
|
||||
permissionValues["dangerously_skip_permissions"] = profileInfo.DangerouslySkipPermissions
|
||||
}
|
||||
// Allow model override from request (for dynamic model switching)
|
||||
if req.ModelOverride != "" {
|
||||
model = req.ModelOverride
|
||||
}
|
||||
cmdOpts := CommandOptions{
|
||||
Model: model,
|
||||
SessionID: req.ACPSessionID,
|
||||
AutoApprove: autoApprove,
|
||||
Model: model,
|
||||
SessionID: req.ACPSessionID,
|
||||
AutoApprove: autoApprove,
|
||||
PermissionValues: permissionValues,
|
||||
}
|
||||
execution.AgentCommand = m.commandBuilder.BuildCommandString(agentConfig, cmdOpts)
|
||||
|
||||
@@ -619,8 +621,22 @@ func (m *Manager) StartAgentProcess(ctx context.Context, executionID string) err
|
||||
env["TASK_DESCRIPTION"] = taskDescription
|
||||
}
|
||||
|
||||
// Determine approval policy for Codex
|
||||
// If auto_approve is true, use "never" (no approval needed)
|
||||
// Otherwise use "untrusted" (request approval for all actions)
|
||||
approvalPolicy := ""
|
||||
if execution.AgentProfileID != "" && m.profileResolver != nil {
|
||||
if profileInfo, err := m.profileResolver.ResolveProfile(ctx, execution.AgentProfileID); err == nil {
|
||||
if profileInfo.AutoApprove {
|
||||
approvalPolicy = "never"
|
||||
} else {
|
||||
approvalPolicy = "untrusted"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the agent command
|
||||
if err := execution.agentctl.ConfigureAgent(ctx, execution.AgentCommand, env); err != nil {
|
||||
if err := execution.agentctl.ConfigureAgent(ctx, execution.AgentCommand, env, approvalPolicy); err != nil {
|
||||
return fmt.Errorf("failed to configure agent: %w", err)
|
||||
}
|
||||
|
||||
@@ -794,10 +810,9 @@ func (m *Manager) getAgentConfigForExecution(execution *AgentExecution) (*regist
|
||||
return nil, fmt.Errorf("failed to resolve profile: %w", err)
|
||||
}
|
||||
|
||||
// Map agent name to registry ID (e.g., "auggie" -> "auggie-agent")
|
||||
agentTypeName := profileInfo.AgentName + "-agent"
|
||||
agentConfig, err := m.registry.Get(agentTypeName)
|
||||
if err != nil {
|
||||
agentTypeName := profileInfo.AgentName
|
||||
agentConfig, ok := m.registry.Get(agentTypeName)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("agent type not found: %s", agentTypeName)
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ func (r *StoreProfileResolver) ResolveProfile(ctx context.Context, profileID str
|
||||
Model: profile.Model,
|
||||
AutoApprove: profile.AutoApprove,
|
||||
DangerouslySkipPermissions: profile.DangerouslySkipPermissions,
|
||||
AllowIndexing: profile.AllowIndexing,
|
||||
Plan: profile.Plan,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ type AgentProfileInfo struct {
|
||||
Model string
|
||||
AutoApprove bool
|
||||
DangerouslySkipPermissions bool
|
||||
AllowIndexing bool
|
||||
Plan string
|
||||
}
|
||||
|
||||
@@ -129,7 +130,7 @@ type WorkspaceInfo struct {
|
||||
SessionID string // Task session ID (from task_sessions table)
|
||||
WorkspacePath string // Path to the workspace/repository
|
||||
AgentProfileID string // Optional - agent profile for the task
|
||||
AgentID string // Agent type ID (e.g., "auggie-agent") - required for runtime creation
|
||||
AgentID string // Agent type ID (e.g., "auggie", "codex") - required for runtime creation
|
||||
ACPSessionID string // Agent's session ID for conversation resumption (from session metadata)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,26 +3,41 @@
|
||||
"version": "1.0",
|
||||
"agents": [
|
||||
{
|
||||
"id": "auggie-agent",
|
||||
"id": "auggie",
|
||||
"name": "Augment Coding Agent",
|
||||
"display_name": "Auggie",
|
||||
"description": "Auggie CLI-powered autonomous coding agent.",
|
||||
"enabled": true,
|
||||
"image": "kandev/augment-agent",
|
||||
"tag": "latest",
|
||||
"cmd": ["auggie", "--acp"],
|
||||
"cmd": [
|
||||
"auggie",
|
||||
"--acp"
|
||||
],
|
||||
"working_dir": "/workspace",
|
||||
"required_env": ["AUGMENT_SESSION_AUTH"],
|
||||
"required_env": [
|
||||
"AUGMENT_SESSION_AUTH"
|
||||
],
|
||||
"env": {},
|
||||
"mounts": [
|
||||
{"source": "{workspace}", "target": "/workspace", "read_only": false}
|
||||
{
|
||||
"source": "{workspace}",
|
||||
"target": "/workspace",
|
||||
"read_only": false
|
||||
}
|
||||
],
|
||||
"resource_limits": {
|
||||
"memory_mb": 4096,
|
||||
"cpu_cores": 2.0,
|
||||
"timeout_seconds": 3600
|
||||
},
|
||||
"capabilities": ["code_generation", "code_review", "refactoring", "testing", "shell_execution"],
|
||||
"capabilities": [
|
||||
"code_generation",
|
||||
"code_review",
|
||||
"refactoring",
|
||||
"testing",
|
||||
"shell_execution"
|
||||
],
|
||||
"protocol": "acp",
|
||||
"protocol_config": {},
|
||||
"model_flag": "--model {model}",
|
||||
@@ -43,6 +58,24 @@
|
||||
"remove-files"
|
||||
]
|
||||
},
|
||||
"permission_settings": {
|
||||
"auto_approve": {
|
||||
"supported": true,
|
||||
"default": true,
|
||||
"label": "Auto-approve",
|
||||
"description": "Automatically approve tool calls",
|
||||
"apply_method": "",
|
||||
"cli_flag": ""
|
||||
},
|
||||
"allow_indexing": {
|
||||
"supported": true,
|
||||
"default": true,
|
||||
"label": "Allow indexing",
|
||||
"description": "Enable workspace indexing without confirmation",
|
||||
"apply_method": "cli_flag",
|
||||
"cli_flag": "--allow-indexing"
|
||||
}
|
||||
},
|
||||
"discovery": {
|
||||
"supports_mcp": true,
|
||||
"mcp_config_path": {
|
||||
@@ -51,9 +84,15 @@
|
||||
"macos": []
|
||||
},
|
||||
"installation_path": {
|
||||
"linux": ["~/.augment/.auggie.json"],
|
||||
"windows": ["~/.augment/.auggie.json"],
|
||||
"macos": ["~/.augment/.auggie.json"]
|
||||
"linux": [
|
||||
"~/.augment/.auggie.json"
|
||||
],
|
||||
"windows": [
|
||||
"~/.augment/.auggie.json"
|
||||
],
|
||||
"macos": [
|
||||
"~/.augment/.auggie.json"
|
||||
]
|
||||
},
|
||||
"discovery_capabilities": {
|
||||
"supports_session_resume": false,
|
||||
@@ -110,26 +149,39 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "codex-agent",
|
||||
"id": "codex",
|
||||
"name": "OpenAI Codex Agent",
|
||||
"display_name": "Codex",
|
||||
"description": "OpenAI Codex CLI-powered autonomous coding agent using the Codex app-server protocol.",
|
||||
"enabled": true,
|
||||
"image": "kandev/codex-agent",
|
||||
"tag": "latest",
|
||||
"cmd": ["codex", "app-server"],
|
||||
"cmd": [
|
||||
"codex",
|
||||
"app-server"
|
||||
],
|
||||
"working_dir": "/workspace",
|
||||
"required_env": [],
|
||||
"env": {},
|
||||
"mounts": [
|
||||
{"source": "{workspace}", "target": "/workspace", "read_only": false}
|
||||
{
|
||||
"source": "{workspace}",
|
||||
"target": "/workspace",
|
||||
"read_only": false
|
||||
}
|
||||
],
|
||||
"resource_limits": {
|
||||
"memory_mb": 4096,
|
||||
"cpu_cores": 2.0,
|
||||
"timeout_seconds": 3600
|
||||
},
|
||||
"capabilities": ["code_generation", "code_review", "refactoring", "testing", "shell_execution"],
|
||||
"capabilities": [
|
||||
"code_generation",
|
||||
"code_review",
|
||||
"refactoring",
|
||||
"testing",
|
||||
"shell_execution"
|
||||
],
|
||||
"protocol": "codex",
|
||||
"protocol_config": {},
|
||||
"model_flag": "-c model=\"{model}\"",
|
||||
@@ -145,6 +197,16 @@
|
||||
"permission_flag": "",
|
||||
"tools_requiring_permission": []
|
||||
},
|
||||
"permission_settings": {
|
||||
"auto_approve": {
|
||||
"supported": true,
|
||||
"default": true,
|
||||
"label": "Auto-approve",
|
||||
"description": "Skip approval requests from Codex",
|
||||
"apply_method": "acp",
|
||||
"cli_flag": ""
|
||||
}
|
||||
},
|
||||
"discovery": {
|
||||
"supports_mcp": true,
|
||||
"mcp_config_path": {
|
||||
@@ -153,9 +215,15 @@
|
||||
"macos": []
|
||||
},
|
||||
"installation_path": {
|
||||
"linux": ["~/.codex/auth.json"],
|
||||
"windows": ["~/.codex/auth.json"],
|
||||
"macos": ["~/.codex/auth.json"]
|
||||
"linux": [
|
||||
"~/.codex/auth.json"
|
||||
],
|
||||
"windows": [
|
||||
"~/.codex/auth.json"
|
||||
],
|
||||
"macos": [
|
||||
"~/.codex/auth.json"
|
||||
]
|
||||
},
|
||||
"discovery_capabilities": {
|
||||
"supports_session_resume": false,
|
||||
@@ -203,4 +271,3 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,9 @@ type AgentTypeConfig struct {
|
||||
// Permission configuration
|
||||
PermissionConfig PermissionConfig `json:"permission_config,omitempty"` // How to handle tool permissions
|
||||
|
||||
// Permission settings metadata (what settings are supported for this agent)
|
||||
PermissionSettings map[string]PermissionSetting `json:"permission_settings,omitempty"` // Supported permission settings
|
||||
|
||||
// Discovery configuration (for detecting agent installation)
|
||||
Discovery DiscoveryConfig `json:"discovery,omitempty"` // Discovery metadata for agent detection
|
||||
|
||||
@@ -112,6 +115,33 @@ type PermissionConfig struct {
|
||||
ToolsRequiringPermission []string `json:"tools_requiring_permission,omitempty"`
|
||||
}
|
||||
|
||||
// PermissionSetting defines metadata for a permission setting option
|
||||
type PermissionSetting struct {
|
||||
// Supported indicates whether this setting is available for the agent
|
||||
Supported bool `json:"supported"`
|
||||
|
||||
// Default is the default value for this setting when creating new profiles
|
||||
Default bool `json:"default"`
|
||||
|
||||
// Label is the display label for the UI
|
||||
Label string `json:"label"`
|
||||
|
||||
// Description explains what the setting does
|
||||
Description string `json:"description"`
|
||||
|
||||
// ApplyMethod defines how this setting is applied: "cli_flag", "acp", "config", or empty (none)
|
||||
ApplyMethod string `json:"apply_method,omitempty"`
|
||||
|
||||
// CLIFlag is the CLI flag to use when ApplyMethod is "cli_flag"
|
||||
// Example: "--allow-indexing" for auggie
|
||||
CLIFlag string `json:"cli_flag,omitempty"`
|
||||
|
||||
// CLIFlagValue is an optional value template for the CLI flag
|
||||
// If empty and the setting is true, just appends the flag
|
||||
// If set, appends "flag value" (e.g., "--approval-policy never")
|
||||
CLIFlagValue string `json:"cli_flag_value,omitempty"`
|
||||
}
|
||||
|
||||
// MountTemplate defines a mount with template support
|
||||
type MountTemplate struct {
|
||||
Source string `json:"source"` // Can use {workspace}, {task_id}
|
||||
@@ -253,26 +283,22 @@ func (r *Registry) Unregister(id string) error {
|
||||
}
|
||||
|
||||
// Get returns an agent type configuration
|
||||
func (r *Registry) Get(id string) (*AgentTypeConfig, error) {
|
||||
func (r *Registry) Get(id string) (*AgentTypeConfig, bool) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
config, exists := r.agents[id]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("agent type %q not found", id)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
return config, exists
|
||||
}
|
||||
|
||||
// GetDefault returns the default agent type configuration.
|
||||
// It tries "augment-agent" first, then falls back to the first enabled agent.
|
||||
// It tries "auggie" first, then falls back to the first enabled agent.
|
||||
func (r *Registry) GetDefault() (*AgentTypeConfig, error) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
// Try augment-agent first
|
||||
if config, exists := r.agents["augment-agent"]; exists && config.Enabled {
|
||||
// Try auggie first
|
||||
if config, exists := r.agents["auggie"]; exists && config.Enabled {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -127,18 +127,18 @@ func TestRegistry_Get(t *testing.T) {
|
||||
_ = reg.Register(config)
|
||||
|
||||
// Test successful get
|
||||
got, err := reg.Get("test-agent")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
got, ok := reg.Get("test-agent")
|
||||
if !ok {
|
||||
t.Fatal("expected agent to be found")
|
||||
}
|
||||
if got.ID != config.ID {
|
||||
t.Errorf("expected ID %q, got %q", config.ID, got.ID)
|
||||
}
|
||||
|
||||
// Test not found
|
||||
_, err = reg.Get("non-existent")
|
||||
if err == nil {
|
||||
t.Error("expected error for non-existent agent type")
|
||||
_, ok = reg.Get("non-existent")
|
||||
if ok {
|
||||
t.Error("expected agent to not be found")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/kandev/kandev/internal/agent/discovery"
|
||||
"github.com/kandev/kandev/internal/agent/mcpconfig"
|
||||
"github.com/kandev/kandev/internal/agent/registry"
|
||||
"github.com/kandev/kandev/internal/agent/settings/dto"
|
||||
"github.com/kandev/kandev/internal/agent/settings/models"
|
||||
"github.com/kandev/kandev/internal/agent/settings/store"
|
||||
@@ -30,6 +31,7 @@ const defaultAgentProfileName = "default"
|
||||
type Controller struct {
|
||||
repo store.Repository
|
||||
discovery *discovery.Registry
|
||||
agentRegistry interface{ Get(id string) (*registry.AgentTypeConfig, bool) }
|
||||
sessionChecker SessionChecker
|
||||
mcpService *mcpconfig.Service
|
||||
logger *logger.Logger
|
||||
@@ -39,10 +41,11 @@ type SessionChecker interface {
|
||||
HasActiveTaskSessionsByAgentProfile(ctx context.Context, agentProfileID string) (bool, error)
|
||||
}
|
||||
|
||||
func NewController(repo store.Repository, discoveryRegistry *discovery.Registry, sessionChecker SessionChecker, log *logger.Logger) *Controller {
|
||||
func NewController(repo store.Repository, discoveryRegistry *discovery.Registry, agentRegistry interface{ Get(id string) (*registry.AgentTypeConfig, bool) }, sessionChecker SessionChecker, log *logger.Logger) *Controller {
|
||||
return &Controller{
|
||||
repo: repo,
|
||||
discovery: discoveryRegistry,
|
||||
agentRegistry: agentRegistry,
|
||||
sessionChecker: sessionChecker,
|
||||
mcpService: mcpconfig.NewService(repo),
|
||||
logger: log.WithFields(zap.String("component", "agent-settings-controller")),
|
||||
@@ -106,14 +109,31 @@ func (c *Controller) ListAvailableAgents(ctx context.Context) (*dto.ListAvailabl
|
||||
})
|
||||
}
|
||||
|
||||
// Convert permission settings
|
||||
var permissionSettings map[string]dto.PermissionSettingDTO
|
||||
if agentConfig, ok := c.agentRegistry.Get(def.Name); ok && agentConfig.PermissionSettings != nil {
|
||||
permissionSettings = make(map[string]dto.PermissionSettingDTO, len(agentConfig.PermissionSettings))
|
||||
for key, setting := range agentConfig.PermissionSettings {
|
||||
permissionSettings[key] = dto.PermissionSettingDTO{
|
||||
Supported: setting.Supported,
|
||||
Default: setting.Default,
|
||||
Label: setting.Label,
|
||||
Description: setting.Description,
|
||||
ApplyMethod: setting.ApplyMethod,
|
||||
CLIFlag: setting.CLIFlag,
|
||||
CLIFlagValue: setting.CLIFlagValue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
payload = append(payload, dto.AvailableAgentDTO{
|
||||
Name: availability.Name,
|
||||
DisplayName: displayName,
|
||||
SupportsMCP: availability.SupportsMCP,
|
||||
MCPConfigPath: availability.MCPConfigPath,
|
||||
InstallationPaths: availability.InstallationPaths,
|
||||
Available: availability.Available,
|
||||
MatchedPath: availability.MatchedPath,
|
||||
Name: availability.Name,
|
||||
DisplayName: displayName,
|
||||
SupportsMCP: availability.SupportsMCP,
|
||||
MCPConfigPath: availability.MCPConfigPath,
|
||||
InstallationPaths: availability.InstallationPaths,
|
||||
Available: availability.Available,
|
||||
MatchedPath: availability.MatchedPath,
|
||||
Capabilities: dto.AgentCapabilitiesDTO{
|
||||
SupportsSessionResume: def.Capabilities.SupportsSessionResume,
|
||||
SupportsShell: def.Capabilities.SupportsShell,
|
||||
@@ -123,7 +143,8 @@ func (c *Controller) ListAvailableAgents(ctx context.Context) (*dto.ListAvailabl
|
||||
DefaultModel: def.ModelConfig.DefaultModel,
|
||||
AvailableModels: modelEntries,
|
||||
},
|
||||
UpdatedAt: now,
|
||||
PermissionSettings: permissionSettings,
|
||||
UpdatedAt: now,
|
||||
})
|
||||
}
|
||||
return &dto.ListAvailableAgentsResponse{Agents: payload, Total: len(payload)}, nil
|
||||
@@ -200,12 +221,29 @@ func (c *Controller) EnsureInitialAgentProfiles(ctx context.Context) error {
|
||||
if len(profiles) > 0 {
|
||||
continue
|
||||
}
|
||||
// Get agent config to read permission settings defaults
|
||||
var autoApprove, allowIndexing, skipPermissions bool
|
||||
if agentConfig, ok := c.agentRegistry.Get(agent.Name); ok && agentConfig.PermissionSettings != nil {
|
||||
if setting, exists := agentConfig.PermissionSettings["auto_approve"]; exists {
|
||||
autoApprove = setting.Default
|
||||
}
|
||||
if setting, exists := agentConfig.PermissionSettings["allow_indexing"]; exists {
|
||||
allowIndexing = setting.Default
|
||||
}
|
||||
if setting, exists := agentConfig.PermissionSettings["dangerously_skip_permissions"]; exists {
|
||||
skipPermissions = setting.Default
|
||||
}
|
||||
}
|
||||
|
||||
defaultProfile := &models.AgentProfile{
|
||||
AgentID: agent.ID,
|
||||
Name: defaultAgentProfileName,
|
||||
Model: defaultModel,
|
||||
Plan: "",
|
||||
AgentDisplayName: displayName,
|
||||
AgentID: agent.ID,
|
||||
Name: defaultAgentProfileName,
|
||||
Model: defaultModel,
|
||||
Plan: "",
|
||||
AgentDisplayName: displayName,
|
||||
AutoApprove: autoApprove,
|
||||
AllowIndexing: allowIndexing,
|
||||
DangerouslySkipPermissions: skipPermissions,
|
||||
}
|
||||
if err := c.repo.CreateAgentProfile(ctx, defaultProfile); err != nil {
|
||||
return err
|
||||
@@ -401,6 +439,7 @@ type CreateProfileRequest struct {
|
||||
Model string
|
||||
AutoApprove bool
|
||||
DangerouslySkipPermissions bool
|
||||
AllowIndexing bool
|
||||
Plan string
|
||||
}
|
||||
|
||||
@@ -424,6 +463,7 @@ func (c *Controller) CreateProfile(ctx context.Context, req CreateProfileRequest
|
||||
Model: req.Model,
|
||||
AutoApprove: req.AutoApprove,
|
||||
DangerouslySkipPermissions: req.DangerouslySkipPermissions,
|
||||
AllowIndexing: req.AllowIndexing,
|
||||
Plan: req.Plan,
|
||||
}
|
||||
if err := c.repo.CreateAgentProfile(ctx, profile); err != nil {
|
||||
@@ -439,6 +479,7 @@ type UpdateProfileRequest struct {
|
||||
Model *string
|
||||
AutoApprove *bool
|
||||
DangerouslySkipPermissions *bool
|
||||
AllowIndexing *bool
|
||||
Plan *string
|
||||
}
|
||||
|
||||
@@ -463,6 +504,9 @@ func (c *Controller) UpdateProfile(ctx context.Context, req UpdateProfileRequest
|
||||
if req.DangerouslySkipPermissions != nil {
|
||||
profile.DangerouslySkipPermissions = *req.DangerouslySkipPermissions
|
||||
}
|
||||
if req.AllowIndexing != nil {
|
||||
profile.AllowIndexing = *req.AllowIndexing
|
||||
}
|
||||
if req.Plan != nil {
|
||||
profile.Plan = *req.Plan
|
||||
}
|
||||
@@ -526,6 +570,7 @@ func toProfileDTO(profile *models.AgentProfile) dto.AgentProfileDTO {
|
||||
Model: profile.Model,
|
||||
AutoApprove: profile.AutoApprove,
|
||||
DangerouslySkipPermissions: profile.DangerouslySkipPermissions,
|
||||
AllowIndexing: profile.AllowIndexing,
|
||||
Plan: profile.Plan,
|
||||
CreatedAt: profile.CreatedAt,
|
||||
UpdatedAt: profile.UpdatedAt,
|
||||
|
||||
@@ -14,6 +14,7 @@ type AgentProfileDTO struct {
|
||||
Model string `json:"model"`
|
||||
AutoApprove bool `json:"auto_approve"`
|
||||
DangerouslySkipPermissions bool `json:"dangerously_skip_permissions"`
|
||||
AllowIndexing bool `json:"allow_indexing"`
|
||||
Plan string `json:"plan"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
@@ -68,17 +69,28 @@ type ModelConfigDTO struct {
|
||||
AvailableModels []ModelEntryDTO `json:"available_models"`
|
||||
}
|
||||
|
||||
type PermissionSettingDTO struct {
|
||||
Supported bool `json:"supported"`
|
||||
Default bool `json:"default"`
|
||||
Label string `json:"label"`
|
||||
Description string `json:"description"`
|
||||
ApplyMethod string `json:"apply_method,omitempty"`
|
||||
CLIFlag string `json:"cli_flag,omitempty"`
|
||||
CLIFlagValue string `json:"cli_flag_value,omitempty"`
|
||||
}
|
||||
|
||||
type AvailableAgentDTO struct {
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
SupportsMCP bool `json:"supports_mcp"`
|
||||
MCPConfigPath string `json:"mcp_config_path,omitempty"`
|
||||
InstallationPaths []string `json:"installation_paths,omitempty"`
|
||||
Available bool `json:"available"`
|
||||
MatchedPath string `json:"matched_path,omitempty"`
|
||||
Capabilities AgentCapabilitiesDTO `json:"capabilities"`
|
||||
ModelConfig ModelConfigDTO `json:"model_config"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
SupportsMCP bool `json:"supports_mcp"`
|
||||
MCPConfigPath string `json:"mcp_config_path,omitempty"`
|
||||
InstallationPaths []string `json:"installation_paths,omitempty"`
|
||||
Available bool `json:"available"`
|
||||
MatchedPath string `json:"matched_path,omitempty"`
|
||||
Capabilities AgentCapabilitiesDTO `json:"capabilities"`
|
||||
ModelConfig ModelConfigDTO `json:"model_config"`
|
||||
PermissionSettings map[string]PermissionSettingDTO `json:"permission_settings,omitempty"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type ListAvailableAgentsResponse struct {
|
||||
|
||||
@@ -282,6 +282,7 @@ type createProfileRequest struct {
|
||||
Model string `json:"model"`
|
||||
AutoApprove bool `json:"auto_approve"`
|
||||
DangerouslySkipPermissions bool `json:"dangerously_skip_permissions"`
|
||||
AllowIndexing bool `json:"allow_indexing"`
|
||||
Plan string `json:"plan"`
|
||||
}
|
||||
|
||||
@@ -301,6 +302,7 @@ func (h *Handlers) httpCreateProfile(c *gin.Context) {
|
||||
Model: body.Model,
|
||||
AutoApprove: body.AutoApprove,
|
||||
DangerouslySkipPermissions: body.DangerouslySkipPermissions,
|
||||
AllowIndexing: body.AllowIndexing,
|
||||
Plan: body.Plan,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -322,6 +324,7 @@ type updateProfileRequest struct {
|
||||
Model *string `json:"model,omitempty"`
|
||||
AutoApprove *bool `json:"auto_approve,omitempty"`
|
||||
DangerouslySkipPermissions *bool `json:"dangerously_skip_permissions,omitempty"`
|
||||
AllowIndexing *bool `json:"allow_indexing,omitempty"`
|
||||
Plan *string `json:"plan,omitempty"`
|
||||
}
|
||||
|
||||
@@ -341,6 +344,7 @@ func (h *Handlers) httpUpdateProfile(c *gin.Context) {
|
||||
Model: body.Model,
|
||||
AutoApprove: body.AutoApprove,
|
||||
DangerouslySkipPermissions: body.DangerouslySkipPermissions,
|
||||
AllowIndexing: body.AllowIndexing,
|
||||
Plan: body.Plan,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -20,6 +20,7 @@ type AgentProfile struct {
|
||||
Model string `json:"model"`
|
||||
AutoApprove bool `json:"auto_approve"`
|
||||
DangerouslySkipPermissions bool `json:"dangerously_skip_permissions"`
|
||||
AllowIndexing bool `json:"allow_indexing"`
|
||||
Plan string `json:"plan"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
@@ -56,6 +56,7 @@ func (r *sqliteRepository) initSchema() error {
|
||||
model TEXT NOT NULL CHECK(model != ''),
|
||||
auto_approve INTEGER NOT NULL DEFAULT 0,
|
||||
dangerously_skip_permissions INTEGER NOT NULL DEFAULT 0,
|
||||
allow_indexing INTEGER NOT NULL DEFAULT 1,
|
||||
plan TEXT DEFAULT '',
|
||||
created_at DATETIME NOT NULL,
|
||||
updated_at DATETIME NOT NULL,
|
||||
@@ -247,10 +248,10 @@ func (r *sqliteRepository) CreateAgentProfile(ctx context.Context, profile *mode
|
||||
profile.CreatedAt = now
|
||||
profile.UpdatedAt = now
|
||||
_, err := r.db.ExecContext(ctx, `
|
||||
INSERT INTO agent_profiles (id, agent_id, name, agent_display_name, model, auto_approve, dangerously_skip_permissions, plan, created_at, updated_at, deleted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO agent_profiles (id, agent_id, name, agent_display_name, model, auto_approve, dangerously_skip_permissions, allow_indexing, plan, created_at, updated_at, deleted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`, profile.ID, profile.AgentID, profile.Name, profile.AgentDisplayName, profile.Model, boolToInt(profile.AutoApprove),
|
||||
boolToInt(profile.DangerouslySkipPermissions), profile.Plan, profile.CreatedAt, profile.UpdatedAt, profile.DeletedAt)
|
||||
boolToInt(profile.DangerouslySkipPermissions), boolToInt(profile.AllowIndexing), profile.Plan, profile.CreatedAt, profile.UpdatedAt, profile.DeletedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -258,10 +259,10 @@ func (r *sqliteRepository) UpdateAgentProfile(ctx context.Context, profile *mode
|
||||
profile.UpdatedAt = time.Now().UTC()
|
||||
result, err := r.db.ExecContext(ctx, `
|
||||
UPDATE agent_profiles
|
||||
SET name = ?, agent_display_name = ?, model = ?, auto_approve = ?, dangerously_skip_permissions = ?, plan = ?, updated_at = ?
|
||||
SET name = ?, agent_display_name = ?, model = ?, auto_approve = ?, dangerously_skip_permissions = ?, allow_indexing = ?, plan = ?, updated_at = ?
|
||||
WHERE id = ? AND deleted_at IS NULL
|
||||
`, profile.Name, profile.AgentDisplayName, profile.Model, boolToInt(profile.AutoApprove),
|
||||
boolToInt(profile.DangerouslySkipPermissions), profile.Plan, profile.UpdatedAt, profile.ID)
|
||||
boolToInt(profile.DangerouslySkipPermissions), boolToInt(profile.AllowIndexing), profile.Plan, profile.UpdatedAt, profile.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -289,7 +290,7 @@ func (r *sqliteRepository) DeleteAgentProfile(ctx context.Context, id string) er
|
||||
|
||||
func (r *sqliteRepository) GetAgentProfile(ctx context.Context, id string) (*models.AgentProfile, error) {
|
||||
row := r.db.QueryRowContext(ctx, `
|
||||
SELECT id, agent_id, name, agent_display_name, model, auto_approve, dangerously_skip_permissions, plan, created_at, updated_at, deleted_at
|
||||
SELECT id, agent_id, name, agent_display_name, model, auto_approve, dangerously_skip_permissions, allow_indexing, plan, created_at, updated_at, deleted_at
|
||||
FROM agent_profiles WHERE id = ? AND deleted_at IS NULL
|
||||
`, id)
|
||||
return scanAgentProfile(row)
|
||||
@@ -297,7 +298,7 @@ func (r *sqliteRepository) GetAgentProfile(ctx context.Context, id string) (*mod
|
||||
|
||||
func (r *sqliteRepository) ListAgentProfiles(ctx context.Context, agentID string) ([]*models.AgentProfile, error) {
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT id, agent_id, name, agent_display_name, model, auto_approve, dangerously_skip_permissions, plan, created_at, updated_at, deleted_at
|
||||
SELECT id, agent_id, name, agent_display_name, model, auto_approve, dangerously_skip_permissions, allow_indexing, plan, created_at, updated_at, deleted_at
|
||||
FROM agent_profiles WHERE agent_id = ? AND deleted_at IS NULL ORDER BY created_at DESC
|
||||
`, agentID)
|
||||
if err != nil {
|
||||
@@ -348,6 +349,7 @@ func scanAgentProfile(scanner interface {
|
||||
profile := &models.AgentProfile{}
|
||||
var autoApprove int
|
||||
var skipPermissions int
|
||||
var allowIndexing int
|
||||
if err := scanner.Scan(
|
||||
&profile.ID,
|
||||
&profile.AgentID,
|
||||
@@ -356,6 +358,7 @@ func scanAgentProfile(scanner interface {
|
||||
&profile.Model,
|
||||
&autoApprove,
|
||||
&skipPermissions,
|
||||
&allowIndexing,
|
||||
&profile.Plan,
|
||||
&profile.CreatedAt,
|
||||
&profile.UpdatedAt,
|
||||
@@ -365,6 +368,7 @@ func scanAgentProfile(scanner interface {
|
||||
}
|
||||
profile.AutoApprove = autoApprove == 1
|
||||
profile.DangerouslySkipPermissions = skipPermissions == 1
|
||||
profile.AllowIndexing = allowIndexing == 1
|
||||
return profile, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -99,14 +99,16 @@ func (c *Client) GetStatus(ctx context.Context) (*StatusResponse, error) {
|
||||
return &status, nil
|
||||
}
|
||||
|
||||
// ConfigureAgent configures the agent command. Must be called before Start().
|
||||
func (c *Client) ConfigureAgent(ctx context.Context, command string, env map[string]string) error {
|
||||
// ConfigureAgent configures the agent command and optional approval policy. Must be called before Start().
|
||||
func (c *Client) ConfigureAgent(ctx context.Context, command string, env map[string]string, approvalPolicy string) error {
|
||||
payload := struct {
|
||||
Command string `json:"command"`
|
||||
Env map[string]string `json:"env,omitempty"`
|
||||
Command string `json:"command"`
|
||||
Env map[string]string `json:"env,omitempty"`
|
||||
ApprovalPolicy string `json:"approval_policy,omitempty"`
|
||||
}{
|
||||
Command: command,
|
||||
Env: env,
|
||||
Command: command,
|
||||
Env: env,
|
||||
ApprovalPolicy: approvalPolicy,
|
||||
}
|
||||
|
||||
body, err := json.Marshal(payload)
|
||||
|
||||
@@ -180,8 +180,9 @@ func (s *Server) handleStart(c *gin.Context) {
|
||||
|
||||
// AgentConfigure request/response - configures the agent command before starting
|
||||
type AgentConfigureRequest struct {
|
||||
Command string `json:"command"`
|
||||
Env map[string]string `json:"env,omitempty"`
|
||||
Command string `json:"command"`
|
||||
Env map[string]string `json:"env,omitempty"`
|
||||
ApprovalPolicy string `json:"approval_policy,omitempty"` // For Codex: "untrusted", "on-failure", "on-request", "never"
|
||||
}
|
||||
|
||||
type AgentConfigureResponse struct {
|
||||
@@ -208,7 +209,7 @@ func (s *Server) handleAgentConfigure(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.procMgr.Configure(req.Command, req.Env); err != nil {
|
||||
if err := s.procMgr.Configure(req.Command, req.Env, req.ApprovalPolicy); err != nil {
|
||||
s.logger.Error("failed to configure agent", zap.Error(err), zap.String("command", req.Command))
|
||||
c.JSON(http.StatusInternalServerError, AgentConfigureResponse{
|
||||
Success: false,
|
||||
@@ -217,7 +218,7 @@ func (s *Server) handleAgentConfigure(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
s.logger.Info("agent configured", zap.String("command", req.Command))
|
||||
s.logger.Info("agent configured", zap.String("command", req.Command), zap.String("approval_policy", req.ApprovalPolicy))
|
||||
c.JSON(http.StatusOK, AgentConfigureResponse{
|
||||
Success: true,
|
||||
Message: "agent configured",
|
||||
|
||||
@@ -345,7 +345,7 @@ func lookupEnvValue(env []string, key string) string {
|
||||
|
||||
// Configure sets the agent command and optional environment variables.
|
||||
// This must be called before Start() if the instance was created without a command.
|
||||
func (m *Manager) Configure(command string, env map[string]string) error {
|
||||
func (m *Manager) Configure(command string, env map[string]string, approvalPolicy string) error {
|
||||
m.startMu.Lock()
|
||||
defer m.startMu.Unlock()
|
||||
|
||||
@@ -366,6 +366,11 @@ func (m *Manager) Configure(command string, env map[string]string) error {
|
||||
m.cfg.AgentCommand = command
|
||||
m.cfg.AgentArgs = args
|
||||
|
||||
// Set approval policy if provided (for Codex)
|
||||
if approvalPolicy != "" {
|
||||
m.cfg.ApprovalPolicy = approvalPolicy
|
||||
}
|
||||
|
||||
// Merge additional env vars
|
||||
if len(env) > 0 {
|
||||
for k, v := range env {
|
||||
@@ -376,6 +381,7 @@ func (m *Manager) Configure(command string, env map[string]string) error {
|
||||
m.logger.Info("agent configured",
|
||||
zap.String("command", command),
|
||||
zap.Strings("args", args),
|
||||
zap.String("approval_policy", m.cfg.ApprovalPolicy),
|
||||
zap.Int("env_count", len(env)))
|
||||
|
||||
return nil
|
||||
|
||||
@@ -50,6 +50,7 @@ export async function createAgentAction(payload: {
|
||||
model: string;
|
||||
auto_approve: boolean;
|
||||
dangerously_skip_permissions: boolean;
|
||||
allow_indexing: boolean;
|
||||
plan: string;
|
||||
}>;
|
||||
}): Promise<Agent> {
|
||||
@@ -80,6 +81,7 @@ export async function createAgentProfileAction(
|
||||
model: string;
|
||||
auto_approve: boolean;
|
||||
dangerously_skip_permissions: boolean;
|
||||
allow_indexing: boolean;
|
||||
plan: string;
|
||||
}
|
||||
): Promise<AgentProfile> {
|
||||
@@ -91,7 +93,7 @@ export async function createAgentProfileAction(
|
||||
|
||||
export async function updateAgentProfileAction(
|
||||
id: string,
|
||||
payload: Partial<Pick<AgentProfile, 'name' | 'model' | 'auto_approve' | 'dangerously_skip_permissions' | 'plan'>>
|
||||
payload: Partial<Pick<AgentProfile, 'name' | 'model' | 'auto_approve' | 'dangerously_skip_permissions' | 'allow_indexing' | 'plan'>>
|
||||
): Promise<AgentProfile> {
|
||||
return fetchJson<AgentProfile>(`${apiBaseUrl}/api/v1/agent-profiles/${id}`, {
|
||||
method: 'PATCH',
|
||||
|
||||
@@ -28,13 +28,14 @@ import {
|
||||
updateAgentProfileAction,
|
||||
updateAgentProfileMcpConfigAction,
|
||||
} from '@/app/actions/agents';
|
||||
import type { Agent, AgentDiscovery, AgentProfile, McpServerDef, AvailableAgent } from '@/lib/types/http';
|
||||
import type { Agent, AgentDiscovery, AgentProfile, McpServerDef, AvailableAgent, PermissionSetting } from '@/lib/types/http';
|
||||
import { generateUUID } from '@/lib/utils';
|
||||
import { useAppStore } from '@/components/state-provider';
|
||||
import { useAvailableAgents } from '@/hooks/domains/settings/use-available-agents';
|
||||
import { ProfileMcpConfigCard } from './profile-mcp-config-card';
|
||||
|
||||
type DraftProfile = AgentProfile & {
|
||||
type DraftProfile = Omit<AgentProfile, 'allow_indexing'> & {
|
||||
allow_indexing?: boolean;
|
||||
isNew?: boolean;
|
||||
mcp_config?: {
|
||||
enabled: boolean;
|
||||
@@ -59,14 +60,20 @@ type AgentSetupFormProps = {
|
||||
onToastError: (error: unknown) => void;
|
||||
};
|
||||
|
||||
const createDraftProfile = (agentId: string, agentDisplayName: string, defaultModel: string): DraftProfile => ({
|
||||
const createDraftProfile = (
|
||||
agentId: string,
|
||||
agentDisplayName: string,
|
||||
defaultModel: string,
|
||||
permissionSettings?: Record<string, PermissionSetting>
|
||||
): DraftProfile => ({
|
||||
id: `draft-${generateUUID()}`,
|
||||
agent_id: agentId,
|
||||
name: '',
|
||||
agent_display_name: agentDisplayName,
|
||||
model: defaultModel,
|
||||
auto_approve: false,
|
||||
dangerously_skip_permissions: false,
|
||||
auto_approve: permissionSettings?.auto_approve?.default ?? false,
|
||||
dangerously_skip_permissions: permissionSettings?.dangerously_skip_permissions?.default ?? false,
|
||||
allow_indexing: permissionSettings?.allow_indexing?.default ?? false,
|
||||
plan: '',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
@@ -81,13 +88,18 @@ const cloneAgent = (agent: Agent): DraftAgent => ({
|
||||
profiles: agent.profiles.map((profile) => ({ ...profile })) as DraftProfile[],
|
||||
});
|
||||
|
||||
const ensureProfiles = (agent: DraftAgent, agentDisplayName: string, defaultModel: string): DraftAgent => {
|
||||
const ensureProfiles = (
|
||||
agent: DraftAgent,
|
||||
agentDisplayName: string,
|
||||
defaultModel: string,
|
||||
permissionSettings?: Record<string, PermissionSetting>
|
||||
): DraftAgent => {
|
||||
if (agent.profiles.length > 0) {
|
||||
return agent;
|
||||
}
|
||||
return {
|
||||
...agent,
|
||||
profiles: [createDraftProfile(agent.id, agentDisplayName, defaultModel)],
|
||||
profiles: [createDraftProfile(agent.id, agentDisplayName, defaultModel, permissionSettings)],
|
||||
};
|
||||
};
|
||||
|
||||
@@ -111,12 +123,19 @@ function AgentSetupForm({
|
||||
return draftAgent.profiles.some((profile) => Boolean(profile.mcp_config?.error));
|
||||
}, [draftAgent.profiles]);
|
||||
|
||||
// Get model config for the current agent
|
||||
const currentAgentModelConfig = useMemo(() => {
|
||||
const agent = availableAgents.find((item: AvailableAgent) => item.name === draftAgent.name);
|
||||
return agent?.model_config ?? { default_model: '', available_models: [] };
|
||||
// Get model config and permission settings for the current agent
|
||||
const currentAvailableAgent = useMemo(() => {
|
||||
return availableAgents.find((item: AvailableAgent) => item.name === draftAgent.name) ?? null;
|
||||
}, [availableAgents, draftAgent.name]);
|
||||
|
||||
const currentAgentModelConfig = useMemo(() => {
|
||||
return currentAvailableAgent?.model_config ?? { default_model: '', available_models: [] };
|
||||
}, [currentAvailableAgent]);
|
||||
|
||||
const permissionSettings = useMemo(() => {
|
||||
return currentAvailableAgent?.permission_settings ?? {};
|
||||
}, [currentAvailableAgent]);
|
||||
|
||||
const isProfileDirty = (draft: DraftProfile, saved?: AgentProfile) => {
|
||||
if (!saved) return true;
|
||||
return (
|
||||
@@ -124,6 +143,7 @@ function AgentSetupForm({
|
||||
draft.model !== saved.model ||
|
||||
draft.auto_approve !== saved.auto_approve ||
|
||||
draft.dangerously_skip_permissions !== saved.dangerously_skip_permissions ||
|
||||
draft.allow_indexing !== saved.allow_indexing ||
|
||||
draft.plan !== saved.plan
|
||||
);
|
||||
};
|
||||
@@ -169,7 +189,7 @@ function AgentSetupForm({
|
||||
...current,
|
||||
profiles: [
|
||||
...current.profiles,
|
||||
{ ...createDraftProfile(current.id, resolveDisplayName(current.name), currentAgentModelConfig.default_model), id: draftId },
|
||||
{ ...createDraftProfile(current.id, resolveDisplayName(current.name), currentAgentModelConfig.default_model, permissionSettings), id: draftId },
|
||||
],
|
||||
}));
|
||||
setNewProfileId(draftId);
|
||||
@@ -183,7 +203,7 @@ function AgentSetupForm({
|
||||
profiles:
|
||||
remaining.length > 0
|
||||
? remaining
|
||||
: [createDraftProfile(current.id, resolveDisplayName(current.name), currentAgentModelConfig.default_model)],
|
||||
: [createDraftProfile(current.id, resolveDisplayName(current.name), currentAgentModelConfig.default_model, permissionSettings)],
|
||||
};
|
||||
});
|
||||
if (newProfileId === profileId) {
|
||||
@@ -263,6 +283,7 @@ function AgentSetupForm({
|
||||
model: profile.model,
|
||||
auto_approve: profile.auto_approve,
|
||||
dangerously_skip_permissions: profile.dangerously_skip_permissions,
|
||||
allow_indexing: profile.allow_indexing ?? false,
|
||||
plan: profile.plan,
|
||||
})),
|
||||
});
|
||||
@@ -316,7 +337,7 @@ function AgentSetupForm({
|
||||
});
|
||||
}
|
||||
upsertAgent(created);
|
||||
setDraftAgent(ensureProfiles(cloneAgent(created), resolveDisplayName(created.name), currentAgentModelConfig.default_model));
|
||||
setDraftAgent(ensureProfiles(cloneAgent(created), resolveDisplayName(created.name), currentAgentModelConfig.default_model, permissionSettings));
|
||||
router.replace(`/settings/agents/${encodeURIComponent(created.name)}`);
|
||||
} else {
|
||||
const agentPatch: { workspace_id?: string | null; mcp_config_path?: string | null } = {};
|
||||
@@ -340,6 +361,7 @@ function AgentSetupForm({
|
||||
model: profile.model,
|
||||
auto_approve: profile.auto_approve,
|
||||
dangerously_skip_permissions: profile.dangerously_skip_permissions,
|
||||
allow_indexing: profile.allow_indexing ?? false,
|
||||
plan: profile.plan,
|
||||
});
|
||||
if (profile.mcp_config && profile.mcp_config.dirty && profile.mcp_config.servers.trim()) {
|
||||
@@ -362,6 +384,7 @@ function AgentSetupForm({
|
||||
model: profile.model,
|
||||
auto_approve: profile.auto_approve,
|
||||
dangerously_skip_permissions: profile.dangerously_skip_permissions,
|
||||
allow_indexing: profile.allow_indexing ?? false,
|
||||
plan: profile.plan,
|
||||
});
|
||||
nextProfiles.push(updatedProfile);
|
||||
@@ -383,7 +406,7 @@ function AgentSetupForm({
|
||||
profiles: nextProfiles,
|
||||
};
|
||||
upsertAgent(nextAgent);
|
||||
setDraftAgent(ensureProfiles(cloneAgent(nextAgent), resolveDisplayName(nextAgent.name), currentAgentModelConfig.default_model));
|
||||
setDraftAgent(ensureProfiles(cloneAgent(nextAgent), resolveDisplayName(nextAgent.name), currentAgentModelConfig.default_model, permissionSettings));
|
||||
}
|
||||
setSaveStatus('success');
|
||||
} catch (error) {
|
||||
@@ -444,9 +467,11 @@ function AgentSetupForm({
|
||||
placeholder="Default profile"
|
||||
/>
|
||||
</div>
|
||||
<Button size="sm" variant="ghost" onClick={() => handleRemoveProfile(profile.id)}>
|
||||
Remove
|
||||
</Button>
|
||||
{draftAgent.profiles.length > 1 && (
|
||||
<Button size="sm" variant="ghost" onClick={() => handleRemoveProfile(profile.id)}>
|
||||
Remove
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
@@ -493,35 +518,56 @@ function AgentSetupForm({
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<div className="flex items-center justify-between rounded-md border p-3">
|
||||
<div className="space-y-1">
|
||||
<Label>Auto-approve</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Automatically approve tool calls.
|
||||
</p>
|
||||
{permissionSettings.auto_approve?.supported && (
|
||||
<div className="flex items-center justify-between rounded-md border p-3">
|
||||
<div className="space-y-1">
|
||||
<Label>{permissionSettings.auto_approve.label}</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{permissionSettings.auto_approve.description}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={profile.auto_approve}
|
||||
onCheckedChange={(checked) =>
|
||||
handleProfileChange(profile.id, { auto_approve: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Switch
|
||||
checked={profile.auto_approve}
|
||||
onCheckedChange={(checked) =>
|
||||
handleProfileChange(profile.id, { auto_approve: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between rounded-md border p-3">
|
||||
<div className="space-y-1">
|
||||
<Label>Skip permissions</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Skip permission checks when running tools.
|
||||
</p>
|
||||
{permissionSettings.dangerously_skip_permissions?.supported && (
|
||||
<div className="flex items-center justify-between rounded-md border p-3">
|
||||
<div className="space-y-1">
|
||||
<Label>{permissionSettings.dangerously_skip_permissions.label}</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{permissionSettings.dangerously_skip_permissions.description}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={profile.dangerously_skip_permissions}
|
||||
onCheckedChange={(checked) =>
|
||||
handleProfileChange(profile.id, { dangerously_skip_permissions: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Switch
|
||||
checked={profile.dangerously_skip_permissions}
|
||||
onCheckedChange={(checked) =>
|
||||
handleProfileChange(profile.id, { dangerously_skip_permissions: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{permissionSettings.allow_indexing?.supported && (
|
||||
<div className="flex items-center justify-between rounded-md border p-3">
|
||||
<div className="space-y-1">
|
||||
<Label>{permissionSettings.allow_indexing.label}</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{permissionSettings.allow_indexing.description}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={profile.allow_indexing ?? false}
|
||||
onCheckedChange={(checked) =>
|
||||
handleProfileChange(profile.id, { allow_indexing: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ProfileMcpConfigCard
|
||||
@@ -570,12 +616,16 @@ export default function AgentSetupPage() {
|
||||
|
||||
const initialAgent = useMemo(() => {
|
||||
if (!decodedKey) return null;
|
||||
const resolveAvailableAgent = (name: string) =>
|
||||
availableAgents.find((item: AvailableAgent) => item.name === name);
|
||||
const resolveDisplayName = (name: string) =>
|
||||
availableAgents.find((item: AvailableAgent) => item.name === name)?.display_name ?? '';
|
||||
resolveAvailableAgent(name)?.display_name ?? '';
|
||||
const resolveDefaultModel = (name: string) =>
|
||||
availableAgents.find((item: AvailableAgent) => item.name === name)?.model_config?.default_model ?? '';
|
||||
resolveAvailableAgent(name)?.model_config?.default_model ?? '';
|
||||
const resolvePermissionSettings = (name: string) =>
|
||||
resolveAvailableAgent(name)?.permission_settings;
|
||||
if (savedAgent) {
|
||||
return ensureProfiles(cloneAgent(savedAgent), resolveDisplayName(savedAgent.name), resolveDefaultModel(savedAgent.name));
|
||||
return ensureProfiles(cloneAgent(savedAgent), resolveDisplayName(savedAgent.name), resolveDefaultModel(savedAgent.name), resolvePermissionSettings(savedAgent.name));
|
||||
}
|
||||
if (discoveryAgent) {
|
||||
const draft: DraftAgent = {
|
||||
@@ -589,7 +639,7 @@ export default function AgentSetupPage() {
|
||||
updated_at: new Date().toISOString(),
|
||||
isNew: true,
|
||||
};
|
||||
return ensureProfiles(draft, resolveDisplayName(draft.name), resolveDefaultModel(draft.name));
|
||||
return ensureProfiles(draft, resolveDisplayName(draft.name), resolveDefaultModel(draft.name), resolvePermissionSettings(draft.name));
|
||||
}
|
||||
return null;
|
||||
}, [decodedKey, discoveryAgent, savedAgent, availableAgents]);
|
||||
|
||||
+13
-5
@@ -3,12 +3,13 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useAvailableAgents } from '@/hooks/domains/settings/use-available-agents';
|
||||
import { useAppStore } from '@/components/state-provider';
|
||||
import type { Agent, AgentProfile, ModelConfig, AvailableAgent } from '@/lib/types/http';
|
||||
import type { Agent, AgentProfile, ModelConfig, AvailableAgent, PermissionSetting } from '@/lib/types/http';
|
||||
|
||||
type AgentProfileSettingsResult = {
|
||||
agent: Agent | null;
|
||||
profile: AgentProfile | null;
|
||||
modelConfig: ModelConfig;
|
||||
permissionSettings: Record<string, PermissionSetting>;
|
||||
};
|
||||
|
||||
export function useAgentProfileSettings(
|
||||
@@ -26,10 +27,17 @@ export function useAgentProfileSettings(
|
||||
return agent?.profiles.find((item: AgentProfile) => item.id === profileId) ?? null;
|
||||
}, [agent?.profiles, profileId]);
|
||||
|
||||
const modelConfig = useMemo(() => {
|
||||
const availableAgent = availableAgents.find((item: AvailableAgent) => item.name === agent?.name);
|
||||
return availableAgent?.model_config ?? { default_model: '', available_models: [] };
|
||||
const availableAgent = useMemo(() => {
|
||||
return availableAgents.find((item: AvailableAgent) => item.name === agent?.name) ?? null;
|
||||
}, [availableAgents, agent?.name]);
|
||||
|
||||
return { agent, profile, modelConfig };
|
||||
const modelConfig = useMemo(() => {
|
||||
return availableAgent?.model_config ?? { default_model: '', available_models: [] };
|
||||
}, [availableAgent]);
|
||||
|
||||
const permissionSettings = useMemo(() => {
|
||||
return availableAgent?.permission_settings ?? {};
|
||||
}, [availableAgent]);
|
||||
|
||||
return { agent, profile, modelConfig, permissionSettings };
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { Textarea } from '@kandev/ui/textarea';
|
||||
import { useToast } from '@/components/toast-provider';
|
||||
import { UnsavedChangesBadge, UnsavedSaveButton } from '@/components/settings/unsaved-indicator';
|
||||
import { deleteAgentProfileAction, updateAgentProfileAction } from '@/app/actions/agents';
|
||||
import type { Agent, AgentProfile, ModelConfig } from '@/lib/types/http';
|
||||
import type { Agent, AgentProfile, ModelConfig, PermissionSetting } from '@/lib/types/http';
|
||||
import { useAppStore } from '@/components/state-provider';
|
||||
import { ProfileMcpConfigCard } from '@/app/settings/agents/[agentId]/profile-mcp-config-card';
|
||||
import type { AgentProfileMcpConfig } from '@/lib/types/http';
|
||||
@@ -26,10 +26,11 @@ type ProfileEditorProps = {
|
||||
agent: Agent;
|
||||
profile: AgentProfile;
|
||||
modelConfig: ModelConfig;
|
||||
permissionSettings: Record<string, PermissionSetting>;
|
||||
initialMcpConfig?: AgentProfileMcpConfig | null;
|
||||
};
|
||||
|
||||
function ProfileEditor({ agent, profile, modelConfig, initialMcpConfig }: ProfileEditorProps) {
|
||||
function ProfileEditor({ agent, profile, modelConfig, permissionSettings, initialMcpConfig }: ProfileEditorProps) {
|
||||
const { toast } = useToast();
|
||||
const settingsAgents = useAppStore((state) => state.settingsAgents.items);
|
||||
const setSettingsAgents = useAppStore((state) => state.setSettingsAgents);
|
||||
@@ -56,6 +57,7 @@ function ProfileEditor({ agent, profile, modelConfig, initialMcpConfig }: Profil
|
||||
draft.model !== savedProfile.model ||
|
||||
draft.auto_approve !== savedProfile.auto_approve ||
|
||||
draft.dangerously_skip_permissions !== savedProfile.dangerously_skip_permissions ||
|
||||
draft.allow_indexing !== savedProfile.allow_indexing ||
|
||||
draft.plan !== savedProfile.plan
|
||||
);
|
||||
}, [draft, savedProfile]);
|
||||
@@ -84,6 +86,7 @@ function ProfileEditor({ agent, profile, modelConfig, initialMcpConfig }: Profil
|
||||
model: draft.model,
|
||||
auto_approve: draft.auto_approve,
|
||||
dangerously_skip_permissions: draft.dangerously_skip_permissions,
|
||||
allow_indexing: draft.allow_indexing,
|
||||
plan: draft.plan,
|
||||
});
|
||||
setSavedProfile(updated);
|
||||
@@ -211,33 +214,56 @@ function ProfileEditor({ agent, profile, modelConfig, initialMcpConfig }: Profil
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<div className="flex items-center justify-between rounded-md border p-3">
|
||||
<div className="space-y-1">
|
||||
<Label>Auto-approve</Label>
|
||||
<p className="text-xs text-muted-foreground">Automatically approve tool calls.</p>
|
||||
{permissionSettings.auto_approve?.supported && (
|
||||
<div className="flex items-center justify-between rounded-md border p-3">
|
||||
<div className="space-y-1">
|
||||
<Label>{permissionSettings.auto_approve.label}</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{permissionSettings.auto_approve.description}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={draft.auto_approve}
|
||||
onCheckedChange={(checked) =>
|
||||
setDraft({ ...draft, auto_approve: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Switch
|
||||
checked={draft.auto_approve}
|
||||
onCheckedChange={(checked) =>
|
||||
setDraft({ ...draft, auto_approve: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between rounded-md border p-3">
|
||||
<div className="space-y-1">
|
||||
<Label>Skip permissions</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Skip permission checks when running tools.
|
||||
</p>
|
||||
{permissionSettings.dangerously_skip_permissions?.supported && (
|
||||
<div className="flex items-center justify-between rounded-md border p-3">
|
||||
<div className="space-y-1">
|
||||
<Label>{permissionSettings.dangerously_skip_permissions.label}</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{permissionSettings.dangerously_skip_permissions.description}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={draft.dangerously_skip_permissions}
|
||||
onCheckedChange={(checked) =>
|
||||
setDraft({ ...draft, dangerously_skip_permissions: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Switch
|
||||
checked={draft.dangerously_skip_permissions}
|
||||
onCheckedChange={(checked) =>
|
||||
setDraft({ ...draft, dangerously_skip_permissions: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{permissionSettings.allow_indexing?.supported && (
|
||||
<div className="flex items-center justify-between rounded-md border p-3">
|
||||
<div className="space-y-1">
|
||||
<Label>{permissionSettings.allow_indexing.label}</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{permissionSettings.allow_indexing.description}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={draft.allow_indexing}
|
||||
onCheckedChange={(checked) =>
|
||||
setDraft({ ...draft, allow_indexing: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -284,7 +310,7 @@ export function AgentProfilePage({ initialMcpConfig }: AgentProfilePageClientPro
|
||||
const profileParam = Array.isArray(params.profileId) ? params.profileId[0] : params.profileId;
|
||||
const agentKey = decodeURIComponent(agentParam ?? '');
|
||||
const profileId = profileParam ?? '';
|
||||
const { agent, profile, modelConfig } = useAgentProfileSettings(agentKey, profileId);
|
||||
const { agent, profile, modelConfig, permissionSettings } = useAgentProfileSettings(agentKey, profileId);
|
||||
|
||||
if (!agent || !profile) {
|
||||
return (
|
||||
@@ -305,6 +331,7 @@ export function AgentProfilePage({ initialMcpConfig }: AgentProfilePageClientPro
|
||||
agent={agent}
|
||||
profile={profile}
|
||||
modelConfig={modelConfig}
|
||||
permissionSettings={permissionSettings}
|
||||
initialMcpConfig={initialMcpConfig}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -52,7 +52,7 @@ export function ChatMessage({ comment, label, className, showRichBlocks }: ChatM
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm, remarkBreaks]}
|
||||
components={{
|
||||
code: ({ node, className, children, ...props }) => {
|
||||
code: ({ className, children, ...props }) => {
|
||||
// Check if it's inline code (no className means inline, className with language-* means code block)
|
||||
const isInline = !className || !className.startsWith('language-');
|
||||
|
||||
@@ -93,7 +93,7 @@ export function ChatMessage({ comment, label, className, showRichBlocks }: ChatM
|
||||
</code>
|
||||
);
|
||||
},
|
||||
ol: ({ node, children, ...props }) => (
|
||||
ol: ({ children, ...props }) => (
|
||||
<ol
|
||||
style={{
|
||||
listStyleType: 'decimal',
|
||||
@@ -106,7 +106,7 @@ export function ChatMessage({ comment, label, className, showRichBlocks }: ChatM
|
||||
{children}
|
||||
</ol>
|
||||
),
|
||||
ul: ({ node, children, ...props }) => (
|
||||
ul: ({ children, ...props }) => (
|
||||
<ul
|
||||
style={{
|
||||
listStyleType: 'disc',
|
||||
@@ -119,7 +119,7 @@ export function ChatMessage({ comment, label, className, showRichBlocks }: ChatM
|
||||
{children}
|
||||
</ul>
|
||||
),
|
||||
li: ({ node, children, ...props }) => (
|
||||
li: ({ children, ...props }) => (
|
||||
<li
|
||||
style={{
|
||||
marginTop: '0.375rem',
|
||||
@@ -130,7 +130,7 @@ export function ChatMessage({ comment, label, className, showRichBlocks }: ChatM
|
||||
{children}
|
||||
</li>
|
||||
),
|
||||
p: ({ node, children, ...props }) => (
|
||||
p: ({ children, ...props }) => (
|
||||
<p
|
||||
style={{
|
||||
marginTop: '1rem',
|
||||
|
||||
@@ -256,6 +256,7 @@ export type AgentProfilePayload = {
|
||||
model: string;
|
||||
auto_approve: boolean;
|
||||
dangerously_skip_permissions: boolean;
|
||||
allow_indexing: boolean;
|
||||
plan: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
|
||||
@@ -394,6 +394,7 @@ export type AgentProfile = {
|
||||
model: string;
|
||||
auto_approve: boolean;
|
||||
dangerously_skip_permissions: boolean;
|
||||
allow_indexing: boolean;
|
||||
plan: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
@@ -460,6 +461,16 @@ export type ModelConfig = {
|
||||
available_models: ModelEntry[];
|
||||
};
|
||||
|
||||
export type PermissionSetting = {
|
||||
supported: boolean;
|
||||
default: boolean;
|
||||
label: string;
|
||||
description: string;
|
||||
apply_method?: string;
|
||||
cli_flag?: string;
|
||||
cli_flag_value?: string;
|
||||
};
|
||||
|
||||
export type AvailableAgent = {
|
||||
name: string;
|
||||
display_name: string;
|
||||
@@ -470,6 +481,7 @@ export type AvailableAgent = {
|
||||
matched_path?: string | null;
|
||||
capabilities: AgentCapabilities;
|
||||
model_config: ModelConfig;
|
||||
permission_settings?: Record<string, PermissionSetting>;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ export function registerAgentsHandlers(store: StoreApi<AppState>): WsHandlers {
|
||||
model: message.payload.profile.model,
|
||||
auto_approve: message.payload.profile.auto_approve,
|
||||
dangerously_skip_permissions: message.payload.profile.dangerously_skip_permissions,
|
||||
allow_indexing: message.payload.profile.allow_indexing,
|
||||
plan: message.payload.profile.plan,
|
||||
created_at: message.payload.profile.created_at ?? '',
|
||||
updated_at: message.payload.profile.updated_at ?? '',
|
||||
@@ -83,6 +84,7 @@ export function registerAgentsHandlers(store: StoreApi<AppState>): WsHandlers {
|
||||
model: message.payload.profile.model,
|
||||
auto_approve: message.payload.profile.auto_approve,
|
||||
dangerously_skip_permissions: message.payload.profile.dangerously_skip_permissions,
|
||||
allow_indexing: message.payload.profile.allow_indexing,
|
||||
plan: message.payload.profile.plan,
|
||||
updated_at: message.payload.profile.updated_at ?? profile.updated_at,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user