@agenticmail/enterprise 0.5.30 → 0.5.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2837,6 +2837,355 @@ function GuardrailsSection(props) {
2837
2837
  );
2838
2838
  }
2839
2839
 
2840
+ // ════════════════════════════════════════════════════════════
2841
+ // CONFIGURATION SECTION — Model, Description, Soul, General
2842
+ // ════════════════════════════════════════════════════════════
2843
+
2844
+ function ConfigurationSection(props) {
2845
+ var agentId = props.agentId;
2846
+ var engineAgent = props.engineAgent;
2847
+ var reload = props.reload;
2848
+ var toast = useApp().toast;
2849
+
2850
+ var ea = engineAgent || {};
2851
+ var config = ea.config || {};
2852
+ var identity = config.identity || {};
2853
+ var modelObj = typeof config.model === 'object' ? config.model : {};
2854
+ var modelStr = typeof config.model === 'string' ? config.model : null;
2855
+
2856
+ var _editing = useState(false);
2857
+ var editing = _editing[0]; var setEditing = _editing[1];
2858
+ var _saving = useState(false);
2859
+ var saving = _saving[0]; var setSaving = _saving[1];
2860
+ var _form = useState({});
2861
+ var form = _form[0]; var setForm = _form[1];
2862
+ var _providers = useState([]);
2863
+ var providers = _providers[0]; var setProviders = _providers[1];
2864
+ var _providerModels = useState([]);
2865
+ var providerModels = _providerModels[0]; var setProviderModels = _providerModels[1];
2866
+
2867
+ useEffect(function() {
2868
+ apiCall('/providers').then(function(d) { setProviders(d.providers || []); }).catch(function() {});
2869
+ }, []);
2870
+
2871
+ // Load models when provider changes in edit mode
2872
+ useEffect(function() {
2873
+ if (!editing || !form.provider) return;
2874
+ apiCall('/providers/' + form.provider + '/models').then(function(d) {
2875
+ setProviderModels(d.models || []);
2876
+ }).catch(function() { setProviderModels([]); });
2877
+ }, [editing, form.provider]);
2878
+
2879
+ var startEdit = function() {
2880
+ setForm({
2881
+ provider: modelObj.provider || '',
2882
+ modelId: modelStr || modelObj.modelId || '',
2883
+ thinkingLevel: modelObj.thinkingLevel || 'medium',
2884
+ description: identity.description || config.description || '',
2885
+ soulId: config.soulId || '',
2886
+ });
2887
+ setEditing(true);
2888
+ // Trigger model load
2889
+ if (modelObj.provider) {
2890
+ apiCall('/providers/' + modelObj.provider + '/models').then(function(d) { setProviderModels(d.models || []); }).catch(function() {});
2891
+ }
2892
+ };
2893
+
2894
+ var set = function(k, v) { setForm(function(f) { var n = Object.assign({}, f); n[k] = v; return n; }); };
2895
+
2896
+ var save = function() {
2897
+ setSaving(true);
2898
+ var updates = {
2899
+ model: { provider: form.provider, modelId: form.modelId, thinkingLevel: form.thinkingLevel },
2900
+ description: form.description,
2901
+ soulId: form.soulId || null,
2902
+ identity: Object.assign({}, identity, { description: form.description }),
2903
+ };
2904
+ var isRunning = ea.state === 'running' || ea.state === 'active' || ea.state === 'degraded';
2905
+ var endpoint = isRunning ? '/agents/' + agentId + '/hot-update' : '/agents/' + agentId + '/config';
2906
+ var method = isRunning ? 'POST' : 'PATCH';
2907
+ engineCall(endpoint, { method: method, body: JSON.stringify({ updates: updates, updatedBy: 'dashboard' }) })
2908
+ .then(function() { toast('Configuration saved', 'success'); setEditing(false); setSaving(false); reload(); })
2909
+ .catch(function(err) { toast('Failed to save: ' + err.message, 'error'); setSaving(false); });
2910
+ };
2911
+
2912
+ var configuredProviders = providers.filter(function(p) { return p.configured; });
2913
+
2914
+ var labelStyle = { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 };
2915
+ var inputStyle = { width: '100%', padding: '8px 10px', borderRadius: 6, border: '1px solid var(--border)', background: 'var(--bg-secondary)', color: 'var(--text-primary)', fontSize: 13 };
2916
+ var fieldGroupStyle = { marginBottom: 16 };
2917
+ var rowStyle = { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 };
2918
+
2919
+ if (editing) {
2920
+ return h(Fragment, null,
2921
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
2922
+ h('h3', { style: { margin: 0, fontSize: 16, fontWeight: 600 } }, 'Edit Configuration'),
2923
+ h('div', { style: { display: 'flex', gap: 8 } },
2924
+ h('button', { className: 'btn btn-ghost btn-sm', onClick: function() { setEditing(false); } }, 'Cancel'),
2925
+ h('button', { className: 'btn btn-primary btn-sm', disabled: saving, onClick: save }, saving ? 'Saving...' : 'Save Changes')
2926
+ )
2927
+ ),
2928
+
2929
+ // Model Settings
2930
+ h('div', { className: 'card', style: { padding: 20, marginBottom: 20 } },
2931
+ h('h4', { style: { margin: '0 0 16px', fontSize: 14, fontWeight: 600 } }, 'LLM Model'),
2932
+ h('div', { style: rowStyle },
2933
+ h('div', { style: fieldGroupStyle },
2934
+ h('label', { style: labelStyle }, 'Provider'),
2935
+ h('select', { style: Object.assign({}, inputStyle, { cursor: 'pointer' }), value: form.provider, onChange: function(e) {
2936
+ set('provider', e.target.value);
2937
+ // Reset model when provider changes
2938
+ set('modelId', '');
2939
+ } },
2940
+ h('option', { value: '' }, '-- Select provider --'),
2941
+ configuredProviders.length > 0
2942
+ ? configuredProviders.map(function(p) { return h('option', { key: p.id, value: p.id }, p.name + (p.isLocal ? ' (Local)' : '')); })
2943
+ : providers.map(function(p) { return h('option', { key: p.id, value: p.id }, p.name); })
2944
+ )
2945
+ ),
2946
+ h('div', { style: fieldGroupStyle },
2947
+ h('label', { style: labelStyle }, 'Model'),
2948
+ providerModels.length > 0
2949
+ ? h('select', { style: Object.assign({}, inputStyle, { cursor: 'pointer' }), value: form.modelId, onChange: function(e) { set('modelId', e.target.value); } },
2950
+ h('option', { value: '' }, '-- Select model --'),
2951
+ providerModels.map(function(m) { return h('option', { key: m.id, value: m.id }, m.name || m.id); }),
2952
+ h('option', { value: '_custom' }, 'Custom (enter manually)')
2953
+ )
2954
+ : h('input', { style: inputStyle, value: form.modelId, onChange: function(e) { set('modelId', e.target.value); }, placeholder: 'Enter model ID' })
2955
+ )
2956
+ ),
2957
+ h('div', { style: rowStyle },
2958
+ h('div', { style: fieldGroupStyle },
2959
+ h('label', { style: labelStyle }, 'Thinking Level'),
2960
+ h('select', { style: Object.assign({}, inputStyle, { cursor: 'pointer' }), value: form.thinkingLevel, onChange: function(e) { set('thinkingLevel', e.target.value); } },
2961
+ h('option', { value: 'none' }, 'None'),
2962
+ h('option', { value: 'low' }, 'Low'),
2963
+ h('option', { value: 'medium' }, 'Medium'),
2964
+ h('option', { value: 'high' }, 'High')
2965
+ )
2966
+ ),
2967
+ form.modelId === '_custom' && h('div', { style: fieldGroupStyle },
2968
+ h('label', { style: labelStyle }, 'Custom Model ID'),
2969
+ h('input', { style: inputStyle, value: form.customModelId || '', onChange: function(e) { set('customModelId', e.target.value); }, placeholder: 'my-fine-tuned-model-v2' })
2970
+ )
2971
+ )
2972
+ ),
2973
+
2974
+ // Description
2975
+ h('div', { className: 'card', style: { padding: 20, marginBottom: 20 } },
2976
+ h('h4', { style: { margin: '0 0 16px', fontSize: 14, fontWeight: 600 } }, 'Description'),
2977
+ h('div', { style: fieldGroupStyle },
2978
+ h('label', { style: labelStyle }, 'Agent Description'),
2979
+ h('textarea', { style: Object.assign({}, inputStyle, { minHeight: 80, resize: 'vertical' }), value: form.description, onChange: function(e) { set('description', e.target.value); }, placeholder: 'What does this agent do? What are its responsibilities?' })
2980
+ )
2981
+ ),
2982
+
2983
+ // Soul ID
2984
+ config.soulId && h('div', { className: 'card', style: { padding: 20 } },
2985
+ h('h4', { style: { margin: '0 0 16px', fontSize: 14, fontWeight: 600 } }, 'Role Template'),
2986
+ h('div', { style: fieldGroupStyle },
2987
+ h('label', { style: labelStyle }, 'Soul Template ID'),
2988
+ h('input', { style: inputStyle, value: form.soulId || '', onChange: function(e) { set('soulId', e.target.value); } })
2989
+ )
2990
+ )
2991
+ );
2992
+ }
2993
+
2994
+ // View mode
2995
+ var displayProvider = modelObj.provider || 'Not set';
2996
+ var displayModel = modelStr || modelObj.modelId || 'Not set';
2997
+ var displayThinking = modelObj.thinkingLevel || 'medium';
2998
+ var displayDescription = identity.description || config.description || '';
2999
+ var displaySoulId = config.soulId || '';
3000
+
3001
+ var fieldView = function(label, value) {
3002
+ return h('div', { style: fieldGroupStyle },
3003
+ h('div', { style: labelStyle }, label),
3004
+ h('div', { style: { fontSize: 14, color: 'var(--text-primary)' } }, value || '\u2014')
3005
+ );
3006
+ };
3007
+
3008
+ return h(Fragment, null,
3009
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
3010
+ h('h3', { style: { margin: 0, fontSize: 16, fontWeight: 600 } }, 'Configuration'),
3011
+ h('button', { className: 'btn btn-primary btn-sm', onClick: startEdit }, I.journal(), ' Edit Configuration')
3012
+ ),
3013
+
3014
+ // Model Card
3015
+ h('div', { className: 'card', style: { padding: 20, marginBottom: 20 } },
3016
+ h('h4', { style: { margin: '0 0 16px', fontSize: 14, fontWeight: 600 } }, 'LLM Model'),
3017
+ h('div', { style: rowStyle },
3018
+ fieldView('Provider', h('span', { style: { textTransform: 'capitalize' } }, displayProvider)),
3019
+ fieldView('Model ID', h('span', { style: { fontFamily: 'var(--font-mono, monospace)', fontSize: 13 } }, displayModel))
3020
+ ),
3021
+ h('div', { style: rowStyle },
3022
+ fieldView('Thinking Level', h('span', { className: 'badge badge-' + (displayThinking === 'high' ? 'primary' : displayThinking === 'medium' ? 'info' : displayThinking === 'low' ? 'neutral' : 'neutral'), style: { textTransform: 'capitalize' } }, displayThinking)),
3023
+ h('div')
3024
+ )
3025
+ ),
3026
+
3027
+ // Description Card
3028
+ h('div', { className: 'card', style: { padding: 20, marginBottom: 20 } },
3029
+ h('h4', { style: { margin: '0 0 16px', fontSize: 14, fontWeight: 600 } }, 'Description'),
3030
+ h('div', { style: { fontSize: 14, color: displayDescription ? 'var(--text-primary)' : 'var(--text-muted)', lineHeight: 1.6 } }, displayDescription || 'No description set.')
3031
+ ),
3032
+
3033
+ // Soul Template Card
3034
+ displaySoulId && h('div', { className: 'card', style: { padding: 20 } },
3035
+ h('h4', { style: { margin: '0 0 16px', fontSize: 14, fontWeight: 600 } }, 'Role Template'),
3036
+ h('span', { className: 'badge badge-primary' }, displaySoulId.replace(/-/g, ' ').replace(/\b\w/g, function(c) { return c.toUpperCase(); }))
3037
+ )
3038
+ );
3039
+ }
3040
+
3041
+ // ════════════════════════════════════════════════════════════
3042
+ // SKILLS SECTION — View and manage agent skills
3043
+ // ════════════════════════════════════════════════════════════
3044
+
3045
+ function SkillsSection(props) {
3046
+ var agentId = props.agentId;
3047
+ var engineAgent = props.engineAgent;
3048
+ var reload = props.reload;
3049
+ var toast = useApp().toast;
3050
+
3051
+ var ea = engineAgent || {};
3052
+ var config = ea.config || {};
3053
+ var currentSkills = Array.isArray(config.skills) ? config.skills : [];
3054
+
3055
+ var _editing = useState(false);
3056
+ var editing = _editing[0]; var setEditing = _editing[1];
3057
+ var _saving = useState(false);
3058
+ var saving = _saving[0]; var setSaving = _saving[1];
3059
+ var _selectedSkills = useState(currentSkills);
3060
+ var selectedSkills = _selectedSkills[0]; var setSelectedSkills = _selectedSkills[1];
3061
+ var _allSkills = useState({});
3062
+ var allSkills = _allSkills[0]; var setAllSkills = _allSkills[1];
3063
+ var _suites = useState([]);
3064
+ var suites = _suites[0]; var setSuites = _suites[1];
3065
+ var _skillSearch = useState('');
3066
+ var skillSearch = _skillSearch[0]; var setSkillSearch = _skillSearch[1];
3067
+
3068
+ useEffect(function() {
3069
+ engineCall('/skills/by-category').then(function(d) { setAllSkills(d.categories || {}); }).catch(function() {});
3070
+ engineCall('/skills/suites').then(function(d) { setSuites(d.suites || []); }).catch(function() {});
3071
+ }, []);
3072
+
3073
+ // Reset selected skills when entering edit mode
3074
+ var startEdit = function() {
3075
+ setSelectedSkills(Array.isArray(config.skills) ? config.skills.slice() : []);
3076
+ setEditing(true);
3077
+ };
3078
+
3079
+ var toggleSkill = function(id) {
3080
+ setSelectedSkills(function(prev) {
3081
+ return prev.includes(id) ? prev.filter(function(s) { return s !== id; }) : prev.concat([id]);
3082
+ });
3083
+ };
3084
+
3085
+ var toggleSuite = function(suite) {
3086
+ setSelectedSkills(function(prev) {
3087
+ var allIn = suite.skills.every(function(id) { return prev.includes(id); });
3088
+ if (allIn) return prev.filter(function(id) { return !suite.skills.includes(id); });
3089
+ var merged = prev.slice();
3090
+ suite.skills.forEach(function(id) { if (!merged.includes(id)) merged.push(id); });
3091
+ return merged;
3092
+ });
3093
+ };
3094
+
3095
+ var save = function() {
3096
+ setSaving(true);
3097
+ var updates = { skills: selectedSkills };
3098
+ var isRunning = ea.state === 'running' || ea.state === 'active' || ea.state === 'degraded';
3099
+ var endpoint = isRunning ? '/agents/' + agentId + '/hot-update' : '/agents/' + agentId + '/config';
3100
+ var method = isRunning ? 'POST' : 'PATCH';
3101
+ engineCall(endpoint, { method: method, body: JSON.stringify({ updates: updates, updatedBy: 'dashboard' }) })
3102
+ .then(function() { toast('Skills updated', 'success'); setEditing(false); setSaving(false); reload(); })
3103
+ .catch(function(err) { toast('Failed to save: ' + err.message, 'error'); setSaving(false); });
3104
+ };
3105
+
3106
+ if (editing) {
3107
+ return h(Fragment, null,
3108
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
3109
+ h('h3', { style: { margin: 0, fontSize: 16, fontWeight: 600 } }, 'Edit Skills'),
3110
+ h('div', { style: { display: 'flex', gap: 8 } },
3111
+ h('span', { className: 'badge badge-primary' }, selectedSkills.length + ' selected'),
3112
+ selectedSkills.length > 0 && h('button', { className: 'btn btn-ghost btn-sm', onClick: function() { setSelectedSkills([]); } }, 'Clear all'),
3113
+ h('button', { className: 'btn btn-ghost btn-sm', onClick: function() { setEditing(false); } }, 'Cancel'),
3114
+ h('button', { className: 'btn btn-primary btn-sm', disabled: saving, onClick: save }, saving ? 'Saving...' : 'Save Skills')
3115
+ )
3116
+ ),
3117
+
3118
+ // Suites
3119
+ suites.length > 0 && h('div', { style: { marginBottom: 24 } },
3120
+ h('h4', { style: { fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em', color: 'var(--text-muted)', marginBottom: 8 } }, 'Suites'),
3121
+ h('div', { className: 'suite-grid' }, suites.map(function(s) {
3122
+ var allIn = s.skills.every(function(id) { return selectedSkills.includes(id); });
3123
+ var someIn = s.skills.some(function(id) { return selectedSkills.includes(id); });
3124
+ return h('div', { key: s.id, className: 'suite-card' + (allIn ? ' selected' : someIn ? ' partial' : ''), onClick: function() { toggleSuite(s); }, style: { cursor: 'pointer' } },
3125
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 } },
3126
+ h('span', { style: { fontSize: 20 } }, s.icon),
3127
+ allIn && h('span', { style: { color: 'var(--accent)' } }, I.check())
3128
+ ),
3129
+ h('div', { className: 'suite-name' }, s.name),
3130
+ h('div', { className: 'suite-desc' }, s.skills.length + ' apps')
3131
+ );
3132
+ }))
3133
+ ),
3134
+
3135
+ // Search
3136
+ h('div', { style: { marginBottom: 14 } },
3137
+ h('input', { className: 'input', type: 'text', placeholder: 'Search skills...', value: skillSearch, onChange: function(e) { setSkillSearch(e.target.value); }, style: { maxWidth: 300 } })
3138
+ ),
3139
+
3140
+ // Skills by category
3141
+ Object.entries(allSkills).map(function(entry) {
3142
+ var cat = entry[0]; var skills = entry[1];
3143
+ var filtered = skillSearch ? skills.filter(function(s) { return s.name.toLowerCase().includes(skillSearch.toLowerCase()) || s.description.toLowerCase().includes(skillSearch.toLowerCase()); }) : skills;
3144
+ if (filtered.length === 0) return null;
3145
+ return h('div', { key: cat, style: { marginBottom: 20 } },
3146
+ h('h4', { style: { fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em', color: 'var(--text-muted)', marginBottom: 8 } }, cat.replace(/-/g, ' ')),
3147
+ h('div', { className: 'skill-grid' }, filtered.map(function(s) {
3148
+ var isSelected = selectedSkills.includes(s.id);
3149
+ return h('div', { key: s.id, className: 'skill-card' + (isSelected ? ' selected' : ''), onClick: function() { toggleSkill(s.id); }, style: { cursor: 'pointer' } },
3150
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } },
3151
+ h('span', { className: 'skill-name' }, (s.icon || '') + ' ' + s.name),
3152
+ isSelected && h('span', { style: { color: 'var(--accent)' } }, I.check())
3153
+ ),
3154
+ h('div', { className: 'skill-desc' }, s.description)
3155
+ );
3156
+ }))
3157
+ );
3158
+ })
3159
+ );
3160
+ }
3161
+
3162
+ // View mode
3163
+ return h(Fragment, null,
3164
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
3165
+ h('h3', { style: { margin: 0, fontSize: 16, fontWeight: 600 } }, 'Skills & Capabilities'),
3166
+ h('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
3167
+ h('span', { className: 'badge badge-primary' }, currentSkills.length + ' skills'),
3168
+ h('button', { className: 'btn btn-primary btn-sm', onClick: startEdit }, I.journal(), ' Edit Skills')
3169
+ )
3170
+ ),
3171
+
3172
+ currentSkills.length > 0
3173
+ ? h('div', { className: 'card', style: { padding: 20 } },
3174
+ h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 8 } },
3175
+ currentSkills.map(function(skillId) {
3176
+ return h('div', { key: skillId, style: { display: 'flex', alignItems: 'center', gap: 6, padding: '8px 14px', borderRadius: 8, background: 'var(--accent-soft)', border: '1px solid var(--accent)', fontSize: 13, fontWeight: 500, color: 'var(--accent-text)' } },
3177
+ h('span', null, skillId.replace(/-/g, ' ').replace(/\b\w/g, function(c) { return c.toUpperCase(); }))
3178
+ );
3179
+ })
3180
+ )
3181
+ )
3182
+ : h('div', { className: 'card', style: { padding: 40, textAlign: 'center' } },
3183
+ h('div', { style: { color: 'var(--text-muted)', marginBottom: 12 } }, 'No skills assigned to this agent.'),
3184
+ h('button', { className: 'btn btn-primary btn-sm', onClick: startEdit }, 'Add Skills')
3185
+ )
3186
+ );
3187
+ }
3188
+
2840
3189
  // ════════════════════════════════════════════════════════════
2841
3190
  // DEPLOYMENT SECTION
2842
3191
  // ════════════════════════════════════════════════════════════
@@ -2882,6 +3231,57 @@ function DeploymentSection(props) {
2882
3231
  var deploymentTarget = deployment.target || config.deploymentTarget || '-';
2883
3232
  var modelDisplay = typeof config.model === 'string' ? config.model : (config.model ? (config.model.modelId || config.model.provider || '-') : '-');
2884
3233
 
3234
+ // ─── Deployment Edit State ──────────────────────────────
3235
+ var _editingDeploy = useState(false);
3236
+ var editingDeploy = _editingDeploy[0]; var setEditingDeploy = _editingDeploy[1];
3237
+ var _savingDeploy = useState(false);
3238
+ var savingDeploy = _savingDeploy[0]; var setSavingDeploy = _savingDeploy[1];
3239
+ var _deployForm = useState({});
3240
+ var deployForm = _deployForm[0]; var setDeployForm = _deployForm[1];
3241
+
3242
+ var startDeployEdit = function() {
3243
+ setDeployForm({
3244
+ target: deployment.target || 'docker',
3245
+ region: deployment.region || 'iad',
3246
+ imageTag: (deployment.config?.docker?.tag) || deployment.imageTag || 'latest',
3247
+ memory: (deployment.config?.docker?.memory) || deployment.memory || '512m',
3248
+ cpu: (deployment.config?.docker?.cpu) || deployment.cpu || '0.5',
3249
+ ports: (deployment.config?.docker?.ports || [3000]).join(', '),
3250
+ });
3251
+ setEditingDeploy(true);
3252
+ };
3253
+
3254
+ var saveDeploy = function() {
3255
+ setSavingDeploy(true);
3256
+ var updates = {
3257
+ deployment: {
3258
+ target: deployForm.target,
3259
+ region: deployForm.region,
3260
+ imageTag: deployForm.imageTag,
3261
+ memory: deployForm.memory,
3262
+ cpu: deployForm.cpu,
3263
+ config: {
3264
+ docker: {
3265
+ image: 'agenticmail/agent',
3266
+ tag: deployForm.imageTag,
3267
+ ports: deployForm.ports.split(',').map(function(p) { return parseInt(p.trim()) || 3000; }),
3268
+ memory: deployForm.memory,
3269
+ cpu: deployForm.cpu,
3270
+ restart: 'unless-stopped',
3271
+ }
3272
+ }
3273
+ }
3274
+ };
3275
+ var isRunning = ea.state === 'running' || ea.state === 'active' || ea.state === 'degraded';
3276
+ var endpoint = isRunning ? '/agents/' + agentId + '/hot-update' : '/agents/' + agentId + '/config';
3277
+ var method = isRunning ? 'POST' : 'PATCH';
3278
+ engineCall(endpoint, { method: method, body: JSON.stringify({ updates: updates, updatedBy: 'dashboard' }) })
3279
+ .then(function() { toast('Deployment config saved', 'success'); setEditingDeploy(false); setSavingDeploy(false); reload(); })
3280
+ .catch(function(err) { toast('Failed to save: ' + err.message, 'error'); setSavingDeploy(false); });
3281
+ };
3282
+
3283
+ var setDf = function(k, v) { setDeployForm(function(f) { var n = Object.assign({}, f); n[k] = v; return n; }); };
3284
+
2885
3285
  // ─── Actions ────────────────────────────────────────────
2886
3286
 
2887
3287
  var deploy = function() {
@@ -2923,9 +3323,66 @@ function DeploymentSection(props) {
2923
3323
 
2924
3324
  return h(Fragment, null,
2925
3325
 
3326
+ // ─── Deployment Edit Card ─────────────────────────────
3327
+ editingDeploy && h('div', { className: 'card', style: { marginBottom: 20, border: '2px solid var(--accent)' } },
3328
+ h('div', { className: 'card-header', style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } },
3329
+ h('span', null, 'Edit Deployment Configuration'),
3330
+ h('div', { style: { display: 'flex', gap: 8 } },
3331
+ h('button', { className: 'btn btn-ghost btn-sm', onClick: function() { setEditingDeploy(false); } }, 'Cancel'),
3332
+ h('button', { className: 'btn btn-primary btn-sm', disabled: savingDeploy, onClick: saveDeploy }, savingDeploy ? 'Saving...' : 'Save')
3333
+ )
3334
+ ),
3335
+ h('div', { className: 'card-body' },
3336
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 16 } },
3337
+ h('div', { className: 'form-group' },
3338
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Target'),
3339
+ h('select', { className: 'input', value: deployForm.target, onChange: function(e) { setDf('target', e.target.value); } },
3340
+ h('option', { value: 'fly' }, 'Fly.io'),
3341
+ h('option', { value: 'docker' }, 'Docker'),
3342
+ h('option', { value: 'railway' }, 'Railway'),
3343
+ h('option', { value: 'vps' }, 'VPS / Server'),
3344
+ h('option', { value: 'local' }, 'Local')
3345
+ )
3346
+ ),
3347
+ (deployForm.target === 'fly' || deployForm.target === 'railway') && h('div', { className: 'form-group' },
3348
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Region'),
3349
+ h('select', { className: 'input', value: deployForm.region, onChange: function(e) { setDf('region', e.target.value); } },
3350
+ h('option', { value: 'iad' }, 'Ashburn (iad)'),
3351
+ h('option', { value: 'ord' }, 'Chicago (ord)'),
3352
+ h('option', { value: 'lax' }, 'Los Angeles (lax)'),
3353
+ h('option', { value: 'lhr' }, 'London (lhr)'),
3354
+ h('option', { value: 'ams' }, 'Amsterdam (ams)'),
3355
+ h('option', { value: 'nrt' }, 'Tokyo (nrt)')
3356
+ )
3357
+ )
3358
+ ),
3359
+ (deployForm.target === 'docker' || deployForm.target === 'fly') && h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 16 } },
3360
+ h('div', { className: 'form-group' },
3361
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Image Tag'),
3362
+ h('input', { className: 'input', value: deployForm.imageTag, onChange: function(e) { setDf('imageTag', e.target.value); } })
3363
+ ),
3364
+ h('div', { className: 'form-group' },
3365
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Memory'),
3366
+ h('input', { className: 'input', value: deployForm.memory, onChange: function(e) { setDf('memory', e.target.value); }, placeholder: '512m' })
3367
+ ),
3368
+ h('div', { className: 'form-group' },
3369
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'CPU'),
3370
+ h('input', { className: 'input', value: deployForm.cpu, onChange: function(e) { setDf('cpu', e.target.value); }, placeholder: '0.5' })
3371
+ ),
3372
+ h('div', { className: 'form-group' },
3373
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Ports'),
3374
+ h('input', { className: 'input', value: deployForm.ports, onChange: function(e) { setDf('ports', e.target.value); }, placeholder: '3000' })
3375
+ )
3376
+ )
3377
+ )
3378
+ ),
3379
+
2926
3380
  // ─── Deployment Status Card ─────────────────────────
2927
3381
  h('div', { className: 'card', style: { marginBottom: 20 } },
2928
- h('div', { className: 'card-header' }, h('span', null, 'Deployment Status')),
3382
+ h('div', { className: 'card-header', style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } },
3383
+ h('span', null, 'Deployment Status'),
3384
+ !editingDeploy && h('button', { className: 'btn btn-ghost btn-sm', onClick: startDeployEdit }, I.journal(), ' Edit')
3385
+ ),
2929
3386
  h('div', { className: 'card-body' },
2930
3387
  h('div', { style: { display: 'flex', alignItems: 'center', gap: 16, marginBottom: 16, flexWrap: 'wrap' } },
2931
3388
  h('span', { className: 'status-dot ' + state }),
@@ -3440,7 +3897,7 @@ function AgentDetailPage(props) {
3440
3897
  var _agents = useState([]);
3441
3898
  var agents = _agents[0]; var setAgents = _agents[1];
3442
3899
 
3443
- var TABS = ['overview', 'personal', 'permissions', 'activity', 'communication', 'workforce', 'memory', 'guardrails', 'budget', 'tool-security', 'deployment'];
3900
+ var TABS = ['overview', 'personal', 'configuration', 'skills', 'permissions', 'activity', 'communication', 'workforce', 'memory', 'guardrails', 'budget', 'tool-security', 'deployment'];
3444
3901
  var TAB_LABELS = { 'tool-security': 'Tool Security' };
3445
3902
 
3446
3903
  var load = function() {
@@ -3562,6 +4019,8 @@ function AgentDetailPage(props) {
3562
4019
  // ─── Tab Content ────────────────────────────────────
3563
4020
  tab === 'overview' && h(OverviewSection, { agentId: agentId, agent: agent, engineAgent: engineAgent, profile: profile, reload: load, agents: agents, onBack: onBack }),
3564
4021
  tab === 'personal' && h(PersonalDetailsSection, { agentId: agentId, agent: agent, engineAgent: engineAgent, reload: load }),
4022
+ tab === 'configuration' && h(ConfigurationSection, { agentId: agentId, engineAgent: engineAgent, reload: load }),
4023
+ tab === 'skills' && h(SkillsSection, { agentId: agentId, engineAgent: engineAgent, reload: load }),
3565
4024
  tab === 'permissions' && h(PermissionsSection, { agentId: agentId, profile: profile, reload: load }),
3566
4025
  tab === 'activity' && h(ActivitySection, { agentId: agentId }),
3567
4026
  tab === 'communication' && h(CommunicationSection, { agentId: agentId, agents: agents }),
@@ -447,8 +447,13 @@ export function CreateAgentWizard({ onClose, onCreated, toast }) {
447
447
  displayName: form.name,
448
448
  email: form.email || form.name.toLowerCase().replace(/\s+/g, '.') + '@agenticmail.local',
449
449
  role: form.role,
450
+ description: form.description || '',
451
+ soulId: form.soulId || null,
450
452
  model: { provider: form.provider || 'anthropic', modelId: form.model === 'custom' ? (form.customModelId || form.model) : form.model },
451
453
  deployment: { target: form.deployTarget },
454
+ deployTarget: form.deployTarget,
455
+ skills: form.skills || [],
456
+ knowledgeBases: form.knowledgeBases || [],
452
457
  presetName: form.preset || undefined,
453
458
  permissions: {
454
459
  maxRiskLevel: form.maxRiskLevel,
@@ -470,6 +475,8 @@ export function CreateAgentWizard({ onClose, onCreated, toast }) {
470
475
  maritalStatus: form.maritalStatus || undefined,
471
476
  culturalBackground: form.culturalBackground || undefined,
472
477
  language: form.language,
478
+ personality: form.personality || undefined,
479
+ description: form.description || undefined,
473
480
  traits: form.traits,
474
481
  },
475
482
  }) });
@@ -209,7 +209,7 @@ export function createAgentRoutes(opts: {
209
209
  * Returns both IDs and the full agent object.
210
210
  */
211
211
  router.post('/bridge/agents', async (c) => {
212
- const { orgId, name, email, displayName, role, model, deployment, permissionProfile, presetName, createdBy, persona, permissions: permissionsData } = await c.req.json();
212
+ const { orgId, name, email, displayName, role, model, deployment, permissionProfile, presetName, createdBy, persona, permissions: permissionsData, skills, knowledgeBases, description, soulId, deployTarget } = await c.req.json();
213
213
 
214
214
  if (!name || !orgId) {
215
215
  return c.json({ error: 'name and orgId are required' }, 400);
@@ -218,29 +218,43 @@ export function createAgentRoutes(opts: {
218
218
  const actor = c.req.header('X-User-Id') || createdBy || 'system';
219
219
  const agentId = crypto.randomUUID();
220
220
 
221
- // Build the engine AgentConfig
221
+ // Build the engine AgentConfig — store EVERYTHING from the wizard
222
+ const agentEmail = email || `${name.toLowerCase().replace(/\s+/g, '-')}@agenticmail.local`;
223
+ const agentRole = role || 'assistant';
224
+ const agentDescription = description || persona?.description || '';
225
+
222
226
  const config: any = {
223
227
  id: agentId,
224
228
  name,
225
229
  displayName: displayName || name,
230
+ email: agentEmail,
231
+ role: agentRole,
232
+ description: agentDescription,
233
+ soulId: soulId || null,
226
234
  identity: {
227
- role: role || 'assistant',
228
- personality: 'professional',
229
- ...(persona?.avatar && { avatar: persona.avatar }),
230
- ...(persona?.gender && { gender: persona.gender }),
231
- ...(persona?.dateOfBirth && { dateOfBirth: persona.dateOfBirth }),
232
- ...(persona?.maritalStatus && { maritalStatus: persona.maritalStatus }),
233
- ...(persona?.culturalBackground && { culturalBackground: persona.culturalBackground }),
234
- ...(persona?.language && { language: persona.language }),
235
- ...(persona?.traits && { traits: persona.traits }),
235
+ name,
236
+ displayName: displayName || name,
237
+ email: agentEmail,
238
+ role: agentRole,
239
+ personality: persona?.personality || 'professional',
240
+ description: agentDescription,
241
+ avatar: persona?.avatar || null,
242
+ gender: persona?.gender || '',
243
+ dateOfBirth: persona?.dateOfBirth || '',
244
+ maritalStatus: persona?.maritalStatus || '',
245
+ culturalBackground: persona?.culturalBackground || '',
246
+ language: persona?.language || 'en-us',
247
+ traits: persona?.traits || {},
236
248
  },
237
249
  model: model || {
238
250
  provider: 'anthropic',
239
251
  modelId: 'claude-sonnet-4-5-20250929',
240
252
  thinkingLevel: 'medium',
241
253
  },
254
+ skills: Array.isArray(skills) ? skills : [],
255
+ knowledgeBases: Array.isArray(knowledgeBases) ? knowledgeBases : [],
242
256
  deployment: deployment || {
243
- target: 'docker',
257
+ target: deployTarget || 'docker',
244
258
  config: { docker: { image: 'agenticmail/agent', tag: 'latest', ports: [3000], env: {}, volumes: [], restart: 'unless-stopped' } },
245
259
  },
246
260
  permissionProfileId: permissionProfile || 'default',
@@ -277,9 +291,9 @@ export function createAgentRoutes(opts: {
277
291
  adminAgent = await _adminDb.createAgent({
278
292
  id: agentId,
279
293
  name,
280
- email: email || `${name.toLowerCase().replace(/\s+/g, '-')}@agenticmail.local`,
281
- role: role || 'assistant',
282
- metadata: { engineLinked: true, orgId },
294
+ email: agentEmail,
295
+ role: agentRole,
296
+ metadata: { engineLinked: true, orgId, soulId: soulId || undefined },
283
297
  createdBy: actor,
284
298
  });
285
299
  }
@@ -244,7 +244,8 @@ export class KnowledgeBaseEngine {
244
244
  // Find all KBs this agent has access to
245
245
  const kbs = Array.from(this.knowledgeBases.values()).filter(kb => {
246
246
  if (opts?.kbIds?.length) return opts.kbIds.includes(kb.id);
247
- return kb.agentIds.includes(agentId) || kb.agentIds.length === 0; // Empty = all agents
247
+ const ids = Array.isArray(kb.agentIds) ? kb.agentIds : [];
248
+ return ids.includes(agentId) || ids.length === 0; // Empty = all agents
248
249
  });
249
250
 
250
251
  if (kbs.length === 0) return [];
@@ -329,9 +330,10 @@ export class KnowledgeBaseEngine {
329
330
  }
330
331
 
331
332
  getKnowledgeBasesForAgent(agentId: string): KnowledgeBase[] {
332
- return Array.from(this.knowledgeBases.values()).filter(kb =>
333
- kb.agentIds.includes(agentId) || kb.agentIds.length === 0
334
- );
333
+ return Array.from(this.knowledgeBases.values()).filter(kb => {
334
+ const ids = Array.isArray(kb.agentIds) ? kb.agentIds : [];
335
+ return ids.includes(agentId) || ids.length === 0;
336
+ });
335
337
  }
336
338
 
337
339
  deleteDocument(kbId: string, docId: string): boolean {