@agenticmail/enterprise 0.5.42 → 0.5.44

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.
@@ -219,6 +219,9 @@ function OverviewSection(props) {
219
219
  var createdAt = engineAgent?.createdAt || engineAgent?.created_at || agent?.createdAt;
220
220
  var agentState = engineAgent?.state || engineAgent?.status || agent?.status || 'unknown';
221
221
  var stateColor = { running: 'success', active: 'success', deploying: 'info', starting: 'info', ready: 'primary', degraded: 'warning', error: 'danger', stopped: 'neutral', draft: 'neutral' }[agentState] || 'neutral';
222
+ var resolvedMgr = resolveManager(config, props.agents);
223
+ var managerName = resolvedMgr ? resolvedMgr.name : null;
224
+ var managerEmail = resolvedMgr && resolvedMgr.type === 'external' ? resolvedMgr.email : null;
222
225
 
223
226
  // Personality traits — can be object (keyed) or array
224
227
  var rawTraits = identity.personality_traits || identity.traits || config.personality_traits || {};
@@ -239,7 +242,7 @@ function OverviewSection(props) {
239
242
  // ─── Agent Summary Card ─────────────────────────────
240
243
  h('div', { className: 'card', style: { marginBottom: 20 } },
241
244
  h('div', { className: 'card-body' },
242
- h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 16 } },
245
+ h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 16 } },
243
246
  h('div', null,
244
247
  h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginBottom: 4, textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 600 } }, 'Status'),
245
248
  h('span', { className: 'badge badge-' + stateColor, style: { textTransform: 'capitalize' } }, agentState)
@@ -252,6 +255,15 @@ function OverviewSection(props) {
252
255
  h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginBottom: 4, textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 600 } }, 'Model'),
253
256
  h('span', { style: { fontSize: 13, fontWeight: 500, fontFamily: 'var(--font-mono, monospace)' } }, agentModel)
254
257
  ),
258
+ h('div', null,
259
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginBottom: 4, textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 600 } }, 'Reports To'),
260
+ managerName
261
+ ? h('span', null,
262
+ h('span', { style: { fontSize: 13, fontWeight: 500, color: 'var(--primary)', cursor: 'pointer' }, onClick: function() { if (resolvedMgr && resolvedMgr.type === 'internal') { /* navigate */ } } }, managerName),
263
+ managerEmail && h('div', { style: { fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-mono, monospace)' } }, managerEmail)
264
+ )
265
+ : h('span', { style: { fontSize: 13, color: 'var(--text-muted)' } }, 'No manager')
266
+ ),
255
267
  h('div', null,
256
268
  h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginBottom: 4, textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 600 } }, 'Created'),
257
269
  h('span', { style: { fontSize: 13, fontWeight: 500 } }, createdAt ? new Date(createdAt).toLocaleDateString() : '—')
@@ -3038,6 +3050,263 @@ function ConfigurationSection(props) {
3038
3050
  );
3039
3051
  }
3040
3052
 
3053
+ // ════════════════════════════════════════════════════════════
3054
+ // MANAGER & DAILY CATCH-UP SECTION
3055
+ // ════════════════════════════════════════════════════════════
3056
+
3057
+ var COMMON_TIMEZONES = [
3058
+ 'America/New_York', 'America/Chicago', 'America/Denver', 'America/Los_Angeles',
3059
+ 'America/Toronto', 'America/Vancouver', 'America/Sao_Paulo', 'America/Mexico_City',
3060
+ 'Europe/London', 'Europe/Paris', 'Europe/Berlin', 'Europe/Amsterdam', 'Europe/Madrid',
3061
+ 'Europe/Rome', 'Europe/Zurich', 'Europe/Stockholm', 'Europe/Warsaw', 'Europe/Istanbul',
3062
+ 'Africa/Lagos', 'Africa/Cairo', 'Africa/Johannesburg', 'Africa/Nairobi',
3063
+ 'Asia/Dubai', 'Asia/Kolkata', 'Asia/Singapore', 'Asia/Tokyo', 'Asia/Shanghai',
3064
+ 'Asia/Seoul', 'Asia/Hong_Kong', 'Asia/Bangkok', 'Asia/Jakarta',
3065
+ 'Australia/Sydney', 'Australia/Melbourne', 'Pacific/Auckland'
3066
+ ];
3067
+
3068
+ function resolveManager(config, allAgents) {
3069
+ var mgr = config.manager || {};
3070
+ // Legacy: managerId at top level
3071
+ var legacyId = config.managerId;
3072
+ if (mgr.type === 'external') {
3073
+ return { type: 'external', name: mgr.name || '', email: mgr.email || '' };
3074
+ }
3075
+ var internalId = mgr.agentId || legacyId;
3076
+ if (internalId) {
3077
+ var found = (allAgents || []).find(function(a) { return a.id === internalId; });
3078
+ return { type: 'internal', agentId: internalId, name: found ? (found.config?.identity?.name || found.config?.displayName || found.name || internalId) : internalId };
3079
+ }
3080
+ return null;
3081
+ }
3082
+
3083
+ function ManagerCatchUpSection(props) {
3084
+ var agentId = props.agentId;
3085
+ var engineAgent = props.engineAgent;
3086
+ var allAgents = props.agents || [];
3087
+ var reload = props.reload;
3088
+ var toast = useApp().toast;
3089
+
3090
+ var ea = engineAgent || {};
3091
+ var config = ea.config || {};
3092
+ var catchUp = config.dailyCatchUp || {};
3093
+
3094
+ var resolved = resolveManager(config, allAgents);
3095
+
3096
+ var _editing = useState(false);
3097
+ var editing = _editing[0]; var setEditing = _editing[1];
3098
+ var _saving = useState(false);
3099
+ var saving = _saving[0]; var setSaving = _saving[1];
3100
+ var _form = useState({});
3101
+ var form = _form[0]; var setForm = _form[1];
3102
+
3103
+ var startEdit = function() {
3104
+ setForm({
3105
+ managerType: resolved ? resolved.type : 'none',
3106
+ managerAgentId: resolved && resolved.type === 'internal' ? resolved.agentId : '',
3107
+ managerName: resolved && resolved.type === 'external' ? resolved.name : '',
3108
+ managerEmail: resolved && resolved.type === 'external' ? resolved.email : '',
3109
+ catchUpEnabled: catchUp.enabled !== false && (catchUp.enabled || catchUp.time),
3110
+ catchUpTime: catchUp.time || '09:00',
3111
+ catchUpTimezone: catchUp.timezone || 'America/New_York',
3112
+ });
3113
+ setEditing(true);
3114
+ };
3115
+
3116
+ var set = function(k, v) { setForm(function(f) { var n = Object.assign({}, f); n[k] = v; return n; }); };
3117
+
3118
+ var save = function() {
3119
+ setSaving(true);
3120
+ var updates = {};
3121
+
3122
+ // Build manager object
3123
+ if (form.managerType === 'external') {
3124
+ if (!form.managerName || !form.managerEmail) {
3125
+ toast('Manager name and email are required', 'error');
3126
+ setSaving(false);
3127
+ return;
3128
+ }
3129
+ updates.manager = { type: 'external', name: form.managerName, email: form.managerEmail };
3130
+ updates.managerId = null; // clear legacy
3131
+ } else if (form.managerType === 'internal') {
3132
+ if (!form.managerAgentId) {
3133
+ toast('Select an agent', 'error');
3134
+ setSaving(false);
3135
+ return;
3136
+ }
3137
+ updates.manager = { type: 'internal', agentId: form.managerAgentId };
3138
+ updates.managerId = form.managerAgentId; // keep legacy compat
3139
+ } else {
3140
+ updates.manager = null;
3141
+ updates.managerId = null;
3142
+ }
3143
+
3144
+ // Build dailyCatchUp
3145
+ if (form.catchUpEnabled) {
3146
+ updates.dailyCatchUp = {
3147
+ enabled: true,
3148
+ time: form.catchUpTime,
3149
+ timezone: form.catchUpTimezone,
3150
+ };
3151
+ } else {
3152
+ updates.dailyCatchUp = { enabled: false };
3153
+ }
3154
+
3155
+ var isRunning = ea.state === 'running' || ea.state === 'active' || ea.state === 'degraded';
3156
+ var endpoint = isRunning ? '/agents/' + agentId + '/hot-update' : '/agents/' + agentId + '/config';
3157
+ var method = isRunning ? 'POST' : 'PATCH';
3158
+
3159
+ engineCall(endpoint, { method: method, body: JSON.stringify({ updates: updates, updatedBy: 'dashboard' }) })
3160
+ .then(function() { toast('Manager & catch-up saved', 'success'); setEditing(false); setSaving(false); reload(); })
3161
+ .catch(function(err) { toast('Failed to save: ' + err.message, 'error'); setSaving(false); });
3162
+ };
3163
+
3164
+ var labelStyle = { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 };
3165
+ var inputStyle = { width: '100%', padding: '8px 10px', borderRadius: 6, border: '1px solid var(--border)', background: 'var(--bg-secondary)', color: 'var(--text-primary)', fontSize: 13 };
3166
+ var fieldGroupStyle = { marginBottom: 16 };
3167
+ var rowStyle = { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 };
3168
+
3169
+ // Other agents this agent could report to (exclude self)
3170
+ var otherAgents = allAgents.filter(function(a) { return a.id !== agentId; });
3171
+
3172
+ if (editing) {
3173
+ return h(Fragment, null,
3174
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
3175
+ h('h3', { style: { margin: 0, fontSize: 16, fontWeight: 600 } }, 'Edit Manager & Daily Catch-Up'),
3176
+ h('div', { style: { display: 'flex', gap: 8 } },
3177
+ h('button', { className: 'btn btn-ghost btn-sm', onClick: function() { setEditing(false); } }, 'Cancel'),
3178
+ h('button', { className: 'btn btn-primary btn-sm', disabled: saving, onClick: save }, saving ? 'Saving...' : 'Save Changes')
3179
+ )
3180
+ ),
3181
+
3182
+ // Manager Card
3183
+ h('div', { className: 'card', style: { padding: 20, marginBottom: 20 } },
3184
+ h('h4', { style: { margin: '0 0 16px', fontSize: 14, fontWeight: 600 } }, 'Manager'),
3185
+ h('p', { style: { fontSize: 13, color: 'var(--text-muted)', marginTop: 0, marginBottom: 16 } }, 'Assign a manager this agent reports to. Can be another agent in the system or an external person (name + email).'),
3186
+
3187
+ h('div', { style: fieldGroupStyle },
3188
+ h('label', { style: labelStyle }, 'Manager Type'),
3189
+ h('select', { style: Object.assign({}, inputStyle, { cursor: 'pointer' }), value: form.managerType, onChange: function(e) {
3190
+ set('managerType', e.target.value);
3191
+ if (e.target.value === 'none') { set('managerAgentId', ''); set('managerName', ''); set('managerEmail', ''); }
3192
+ } },
3193
+ h('option', { value: 'none' }, 'No manager'),
3194
+ h('option', { value: 'internal' }, 'Another agent in this organization'),
3195
+ h('option', { value: 'external' }, 'External person (name + email)')
3196
+ )
3197
+ ),
3198
+
3199
+ form.managerType === 'internal' && h('div', { style: fieldGroupStyle },
3200
+ h('label', { style: labelStyle }, 'Select Agent'),
3201
+ h('select', { style: Object.assign({}, inputStyle, { cursor: 'pointer' }), value: form.managerAgentId, onChange: function(e) { set('managerAgentId', e.target.value); } },
3202
+ h('option', { value: '' }, '-- Select agent --'),
3203
+ otherAgents.map(function(a) {
3204
+ var name = a.config?.identity?.name || a.config?.displayName || a.name || a.id;
3205
+ var role = a.config?.identity?.role || a.config?.role || '';
3206
+ return h('option', { key: a.id, value: a.id }, name + (role ? ' (' + role + ')' : ''));
3207
+ })
3208
+ )
3209
+ ),
3210
+
3211
+ form.managerType === 'external' && h(Fragment, null,
3212
+ h('div', { style: rowStyle },
3213
+ h('div', { style: fieldGroupStyle },
3214
+ h('label', { style: labelStyle }, 'Manager Name'),
3215
+ h('input', { style: inputStyle, type: 'text', value: form.managerName, placeholder: 'e.g. Sarah Johnson', onChange: function(e) { set('managerName', e.target.value); } })
3216
+ ),
3217
+ h('div', { style: fieldGroupStyle },
3218
+ h('label', { style: labelStyle }, 'Manager Email'),
3219
+ h('input', { style: inputStyle, type: 'email', value: form.managerEmail, placeholder: 'e.g. sarah@company.com', onChange: function(e) { set('managerEmail', e.target.value); } })
3220
+ )
3221
+ ),
3222
+ h('p', { style: { fontSize: 12, color: 'var(--text-muted)', marginTop: 0, marginBottom: 0 } }, 'The agent will email this person for daily catch-ups, status reports, and escalations.')
3223
+ )
3224
+ ),
3225
+
3226
+ // Daily Catch-Up Card
3227
+ h('div', { className: 'card', style: { padding: 20 } },
3228
+ h('h4', { style: { margin: '0 0 16px', fontSize: 14, fontWeight: 600 } }, 'Daily Catch-Up'),
3229
+ h('p', { style: { fontSize: 13, color: 'var(--text-muted)', marginTop: 0, marginBottom: 16 } }, 'When enabled, the agent sends a daily status email to its manager with goals, progress, and blockers.'),
3230
+
3231
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 10, marginBottom: 16 } },
3232
+ h('label', { style: { display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: 13, fontWeight: 600 } },
3233
+ h('input', { type: 'checkbox', checked: form.catchUpEnabled, onChange: function(e) { set('catchUpEnabled', e.target.checked); } }),
3234
+ 'Enable daily catch-up'
3235
+ )
3236
+ ),
3237
+
3238
+ form.catchUpEnabled && h('div', { style: rowStyle },
3239
+ h('div', { style: fieldGroupStyle },
3240
+ h('label', { style: labelStyle }, 'Time'),
3241
+ h('input', { style: inputStyle, type: 'time', value: form.catchUpTime, onChange: function(e) { set('catchUpTime', e.target.value); } })
3242
+ ),
3243
+ h('div', { style: fieldGroupStyle },
3244
+ h('label', { style: labelStyle }, 'Timezone'),
3245
+ h('select', { style: Object.assign({}, inputStyle, { cursor: 'pointer' }), value: form.catchUpTimezone, onChange: function(e) { set('catchUpTimezone', e.target.value); } },
3246
+ COMMON_TIMEZONES.map(function(tz) { return h('option', { key: tz, value: tz }, tz.replace(/_/g, ' ')); })
3247
+ )
3248
+ )
3249
+ ),
3250
+
3251
+ form.catchUpEnabled && !form.managerType !== 'none' && form.managerType === 'none' && h('div', {
3252
+ style: { padding: '10px 14px', background: 'var(--warning-soft, #fff3cd)', borderRadius: 6, fontSize: 13, color: 'var(--warning-text, #856404)', marginTop: 12 }
3253
+ }, 'Note: Catch-up is enabled but no manager is assigned. The agent won\'t have anyone to report to.')
3254
+ )
3255
+ );
3256
+ }
3257
+
3258
+ // View mode
3259
+ var catchUpEnabled = catchUp.enabled || catchUp.time;
3260
+ var catchUpTime = catchUp.time || '09:00';
3261
+ var catchUpTz = catchUp.timezone || 'America/New_York';
3262
+
3263
+ return h(Fragment, null,
3264
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
3265
+ h('h3', { style: { margin: 0, fontSize: 16, fontWeight: 600 } }, 'Manager & Daily Catch-Up'),
3266
+ h('button', { className: 'btn btn-primary btn-sm', onClick: startEdit }, I.journal(), ' Edit')
3267
+ ),
3268
+
3269
+ // Manager Card
3270
+ h('div', { className: 'card', style: { padding: 20, marginBottom: 20 } },
3271
+ h('h4', { style: { margin: '0 0 16px', fontSize: 14, fontWeight: 600 } }, 'Reports To'),
3272
+ resolved
3273
+ ? h('div', { style: { display: 'flex', alignItems: 'center', gap: 12 } },
3274
+ h('div', { style: {
3275
+ width: 40, height: 40, borderRadius: '50%', background: resolved.type === 'external' ? 'var(--accent)' : 'var(--primary)',
3276
+ display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 16, fontWeight: 700, color: '#fff', flexShrink: 0
3277
+ } }, (resolved.name || '?').charAt(0).toUpperCase()),
3278
+ h('div', null,
3279
+ h('div', { style: { fontSize: 14, fontWeight: 600 } }, resolved.name),
3280
+ resolved.type === 'external'
3281
+ ? h('div', { style: { fontSize: 13, color: 'var(--text-muted)', fontFamily: 'var(--font-mono, monospace)' } }, resolved.email)
3282
+ : h('span', { className: 'badge badge-neutral', style: { fontSize: 11 } }, 'Internal Agent')
3283
+ )
3284
+ )
3285
+ : h('div', { style: { fontSize: 14, color: 'var(--text-muted)' } }, 'No manager assigned')
3286
+ ),
3287
+
3288
+ // Daily Catch-Up Card
3289
+ h('div', { className: 'card', style: { padding: 20 } },
3290
+ h('h4', { style: { margin: '0 0 16px', fontSize: 14, fontWeight: 600 } }, 'Daily Catch-Up'),
3291
+ catchUpEnabled
3292
+ ? h('div', null,
3293
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 } },
3294
+ h('span', { style: { width: 8, height: 8, borderRadius: '50%', background: 'var(--success)', display: 'inline-block' } }),
3295
+ h('span', { style: { fontSize: 14, fontWeight: 600 } }, 'Active')
3296
+ ),
3297
+ h('div', { style: { fontSize: 13, color: 'var(--text-secondary)' } },
3298
+ 'Sends daily at ', h('strong', null, catchUpTime), ' ', catchUpTz.replace(/_/g, ' ')
3299
+ ),
3300
+ !resolved && h('div', { style: { fontSize: 12, color: 'var(--warning-text, #856404)', marginTop: 8 } }, 'Warning: No manager assigned — catch-up emails have no recipient.')
3301
+ )
3302
+ : h('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
3303
+ h('span', { style: { width: 8, height: 8, borderRadius: '50%', background: 'var(--text-muted)', display: 'inline-block' } }),
3304
+ h('span', { style: { fontSize: 14, color: 'var(--text-muted)' } }, 'Not configured')
3305
+ )
3306
+ )
3307
+ );
3308
+ }
3309
+
3041
3310
  // ════════════════════════════════════════════════════════════
3042
3311
  // SKILLS SECTION — View and manage agent skills
3043
3312
  // ════════════════════════════════════════════════════════════
@@ -3897,8 +4166,8 @@ function AgentDetailPage(props) {
3897
4166
  var _agents = useState([]);
3898
4167
  var agents = _agents[0]; var setAgents = _agents[1];
3899
4168
 
3900
- var TABS = ['overview', 'personal', 'configuration', 'skills', 'permissions', 'activity', 'communication', 'workforce', 'memory', 'guardrails', 'budget', 'tool-security', 'deployment'];
3901
- var TAB_LABELS = { 'tool-security': 'Tool Security' };
4169
+ var TABS = ['overview', 'personal', 'configuration', 'manager', 'skills', 'permissions', 'activity', 'communication', 'workforce', 'memory', 'guardrails', 'budget', 'tool-security', 'deployment'];
4170
+ var TAB_LABELS = { 'tool-security': 'Tool Security', 'manager': 'Manager & Catch-Up' };
3902
4171
 
3903
4172
  var load = function() {
3904
4173
  setLoading(true);
@@ -4020,6 +4289,7 @@ function AgentDetailPage(props) {
4020
4289
  tab === 'overview' && h(OverviewSection, { agentId: agentId, agent: agent, engineAgent: engineAgent, profile: profile, reload: load, agents: agents, onBack: onBack }),
4021
4290
  tab === 'personal' && h(PersonalDetailsSection, { agentId: agentId, agent: agent, engineAgent: engineAgent, reload: load }),
4022
4291
  tab === 'configuration' && h(ConfigurationSection, { agentId: agentId, engineAgent: engineAgent, reload: load }),
4292
+ tab === 'manager' && h(ManagerCatchUpSection, { agentId: agentId, engineAgent: engineAgent, agents: agents, reload: load }),
4023
4293
  tab === 'skills' && h(SkillsSection, { agentId: agentId, engineAgent: engineAgent, reload: load }),
4024
4294
  tab === 'permissions' && h(PermissionsSection, { agentId: agentId, profile: profile, reload: load }),
4025
4295
  tab === 'activity' && h(ActivitySection, { agentId: agentId }),
@@ -211,6 +211,18 @@ export function SkillsPage() {
211
211
  setConfigSaving(false);
212
212
  };
213
213
 
214
+ // Install a builtin skill
215
+ var installBuiltinSkill = async function(skillId) {
216
+ try {
217
+ await engineCall('/community/skills/' + skillId + '/install', {
218
+ method: 'POST',
219
+ body: JSON.stringify({ orgId: getOrgId() })
220
+ });
221
+ toast('Skill installed', 'success');
222
+ loadInstalled();
223
+ } catch (e) { toast(e.message || 'Install failed', 'error'); }
224
+ };
225
+
214
226
  // Computed
215
227
  var allSkills = Object.entries(skills).flatMap(function(entry) {
216
228
  return entry[1].map(function(s) { return Object.assign({}, s, { category: entry[0] }); });
@@ -246,11 +258,26 @@ export function SkillsPage() {
246
258
  return h('div', { key: cat, style: { marginBottom: 24 } },
247
259
  h('h3', { style: { fontSize: 13, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)', marginBottom: 10 } }, cat.replace(/-/g, ' ')),
248
260
  h('div', { className: 'skill-grid' }, list.map(function(s) {
261
+ var isInstalled = installed.some(function(i) { return i.skillId === s.id; });
249
262
  return h('div', { key: s.id, className: 'skill-card' },
250
263
  h('div', { className: 'skill-cat' }, s.category || cat),
251
264
  h('div', { className: 'skill-name' }, s.name),
252
265
  h('div', { className: 'skill-desc' }, s.description),
253
- s.tools && h('div', { style: { marginTop: 6, fontSize: 11, color: 'var(--text-muted)' } }, s.tools.length + ' tools')
266
+ s.tools && h('div', { style: { marginTop: 6, fontSize: 11, color: 'var(--text-muted)' } }, s.tools.length + ' tools'),
267
+ h('div', { style: { marginTop: 8, display: 'flex', gap: 6 } },
268
+ isInstalled
269
+ ? h('span', { style: { fontSize: 11, color: 'var(--success)', fontWeight: 600 } }, '\u2713 Installed')
270
+ : h('button', {
271
+ className: 'btn btn-primary btn-sm',
272
+ style: { fontSize: 11, padding: '3px 10px' },
273
+ onClick: function() { installBuiltinSkill(s.id); }
274
+ }, 'Install'),
275
+ !isInstalled && h('button', {
276
+ className: 'btn btn-secondary btn-sm',
277
+ style: { fontSize: 11, padding: '3px 10px' },
278
+ onClick: function() { setTokenModal({ skillId: s.id, skillName: s.name }); }
279
+ }, 'Add Token')
280
+ )
254
281
  );
255
282
  }))
256
283
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/enterprise",
3
- "version": "0.5.42",
3
+ "version": "0.5.44",
4
4
  "description": "AgenticMail Enterprise — cloud-hosted AI agent identity, email, auth & compliance for organizations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -219,6 +219,9 @@ function OverviewSection(props) {
219
219
  var createdAt = engineAgent?.createdAt || engineAgent?.created_at || agent?.createdAt;
220
220
  var agentState = engineAgent?.state || engineAgent?.status || agent?.status || 'unknown';
221
221
  var stateColor = { running: 'success', active: 'success', deploying: 'info', starting: 'info', ready: 'primary', degraded: 'warning', error: 'danger', stopped: 'neutral', draft: 'neutral' }[agentState] || 'neutral';
222
+ var resolvedMgr = resolveManager(config, props.agents);
223
+ var managerName = resolvedMgr ? resolvedMgr.name : null;
224
+ var managerEmail = resolvedMgr && resolvedMgr.type === 'external' ? resolvedMgr.email : null;
222
225
 
223
226
  // Personality traits — can be object (keyed) or array
224
227
  var rawTraits = identity.personality_traits || identity.traits || config.personality_traits || {};
@@ -239,7 +242,7 @@ function OverviewSection(props) {
239
242
  // ─── Agent Summary Card ─────────────────────────────
240
243
  h('div', { className: 'card', style: { marginBottom: 20 } },
241
244
  h('div', { className: 'card-body' },
242
- h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 16 } },
245
+ h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 16 } },
243
246
  h('div', null,
244
247
  h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginBottom: 4, textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 600 } }, 'Status'),
245
248
  h('span', { className: 'badge badge-' + stateColor, style: { textTransform: 'capitalize' } }, agentState)
@@ -252,6 +255,15 @@ function OverviewSection(props) {
252
255
  h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginBottom: 4, textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 600 } }, 'Model'),
253
256
  h('span', { style: { fontSize: 13, fontWeight: 500, fontFamily: 'var(--font-mono, monospace)' } }, agentModel)
254
257
  ),
258
+ h('div', null,
259
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginBottom: 4, textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 600 } }, 'Reports To'),
260
+ managerName
261
+ ? h('span', null,
262
+ h('span', { style: { fontSize: 13, fontWeight: 500, color: 'var(--primary)', cursor: 'pointer' }, onClick: function() { if (resolvedMgr && resolvedMgr.type === 'internal') { /* navigate */ } } }, managerName),
263
+ managerEmail && h('div', { style: { fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-mono, monospace)' } }, managerEmail)
264
+ )
265
+ : h('span', { style: { fontSize: 13, color: 'var(--text-muted)' } }, 'No manager')
266
+ ),
255
267
  h('div', null,
256
268
  h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginBottom: 4, textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 600 } }, 'Created'),
257
269
  h('span', { style: { fontSize: 13, fontWeight: 500 } }, createdAt ? new Date(createdAt).toLocaleDateString() : '—')
@@ -3038,6 +3050,263 @@ function ConfigurationSection(props) {
3038
3050
  );
3039
3051
  }
3040
3052
 
3053
+ // ════════════════════════════════════════════════════════════
3054
+ // MANAGER & DAILY CATCH-UP SECTION
3055
+ // ════════════════════════════════════════════════════════════
3056
+
3057
+ var COMMON_TIMEZONES = [
3058
+ 'America/New_York', 'America/Chicago', 'America/Denver', 'America/Los_Angeles',
3059
+ 'America/Toronto', 'America/Vancouver', 'America/Sao_Paulo', 'America/Mexico_City',
3060
+ 'Europe/London', 'Europe/Paris', 'Europe/Berlin', 'Europe/Amsterdam', 'Europe/Madrid',
3061
+ 'Europe/Rome', 'Europe/Zurich', 'Europe/Stockholm', 'Europe/Warsaw', 'Europe/Istanbul',
3062
+ 'Africa/Lagos', 'Africa/Cairo', 'Africa/Johannesburg', 'Africa/Nairobi',
3063
+ 'Asia/Dubai', 'Asia/Kolkata', 'Asia/Singapore', 'Asia/Tokyo', 'Asia/Shanghai',
3064
+ 'Asia/Seoul', 'Asia/Hong_Kong', 'Asia/Bangkok', 'Asia/Jakarta',
3065
+ 'Australia/Sydney', 'Australia/Melbourne', 'Pacific/Auckland'
3066
+ ];
3067
+
3068
+ function resolveManager(config, allAgents) {
3069
+ var mgr = config.manager || {};
3070
+ // Legacy: managerId at top level
3071
+ var legacyId = config.managerId;
3072
+ if (mgr.type === 'external') {
3073
+ return { type: 'external', name: mgr.name || '', email: mgr.email || '' };
3074
+ }
3075
+ var internalId = mgr.agentId || legacyId;
3076
+ if (internalId) {
3077
+ var found = (allAgents || []).find(function(a) { return a.id === internalId; });
3078
+ return { type: 'internal', agentId: internalId, name: found ? (found.config?.identity?.name || found.config?.displayName || found.name || internalId) : internalId };
3079
+ }
3080
+ return null;
3081
+ }
3082
+
3083
+ function ManagerCatchUpSection(props) {
3084
+ var agentId = props.agentId;
3085
+ var engineAgent = props.engineAgent;
3086
+ var allAgents = props.agents || [];
3087
+ var reload = props.reload;
3088
+ var toast = useApp().toast;
3089
+
3090
+ var ea = engineAgent || {};
3091
+ var config = ea.config || {};
3092
+ var catchUp = config.dailyCatchUp || {};
3093
+
3094
+ var resolved = resolveManager(config, allAgents);
3095
+
3096
+ var _editing = useState(false);
3097
+ var editing = _editing[0]; var setEditing = _editing[1];
3098
+ var _saving = useState(false);
3099
+ var saving = _saving[0]; var setSaving = _saving[1];
3100
+ var _form = useState({});
3101
+ var form = _form[0]; var setForm = _form[1];
3102
+
3103
+ var startEdit = function() {
3104
+ setForm({
3105
+ managerType: resolved ? resolved.type : 'none',
3106
+ managerAgentId: resolved && resolved.type === 'internal' ? resolved.agentId : '',
3107
+ managerName: resolved && resolved.type === 'external' ? resolved.name : '',
3108
+ managerEmail: resolved && resolved.type === 'external' ? resolved.email : '',
3109
+ catchUpEnabled: catchUp.enabled !== false && (catchUp.enabled || catchUp.time),
3110
+ catchUpTime: catchUp.time || '09:00',
3111
+ catchUpTimezone: catchUp.timezone || 'America/New_York',
3112
+ });
3113
+ setEditing(true);
3114
+ };
3115
+
3116
+ var set = function(k, v) { setForm(function(f) { var n = Object.assign({}, f); n[k] = v; return n; }); };
3117
+
3118
+ var save = function() {
3119
+ setSaving(true);
3120
+ var updates = {};
3121
+
3122
+ // Build manager object
3123
+ if (form.managerType === 'external') {
3124
+ if (!form.managerName || !form.managerEmail) {
3125
+ toast('Manager name and email are required', 'error');
3126
+ setSaving(false);
3127
+ return;
3128
+ }
3129
+ updates.manager = { type: 'external', name: form.managerName, email: form.managerEmail };
3130
+ updates.managerId = null; // clear legacy
3131
+ } else if (form.managerType === 'internal') {
3132
+ if (!form.managerAgentId) {
3133
+ toast('Select an agent', 'error');
3134
+ setSaving(false);
3135
+ return;
3136
+ }
3137
+ updates.manager = { type: 'internal', agentId: form.managerAgentId };
3138
+ updates.managerId = form.managerAgentId; // keep legacy compat
3139
+ } else {
3140
+ updates.manager = null;
3141
+ updates.managerId = null;
3142
+ }
3143
+
3144
+ // Build dailyCatchUp
3145
+ if (form.catchUpEnabled) {
3146
+ updates.dailyCatchUp = {
3147
+ enabled: true,
3148
+ time: form.catchUpTime,
3149
+ timezone: form.catchUpTimezone,
3150
+ };
3151
+ } else {
3152
+ updates.dailyCatchUp = { enabled: false };
3153
+ }
3154
+
3155
+ var isRunning = ea.state === 'running' || ea.state === 'active' || ea.state === 'degraded';
3156
+ var endpoint = isRunning ? '/agents/' + agentId + '/hot-update' : '/agents/' + agentId + '/config';
3157
+ var method = isRunning ? 'POST' : 'PATCH';
3158
+
3159
+ engineCall(endpoint, { method: method, body: JSON.stringify({ updates: updates, updatedBy: 'dashboard' }) })
3160
+ .then(function() { toast('Manager & catch-up saved', 'success'); setEditing(false); setSaving(false); reload(); })
3161
+ .catch(function(err) { toast('Failed to save: ' + err.message, 'error'); setSaving(false); });
3162
+ };
3163
+
3164
+ var labelStyle = { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 };
3165
+ var inputStyle = { width: '100%', padding: '8px 10px', borderRadius: 6, border: '1px solid var(--border)', background: 'var(--bg-secondary)', color: 'var(--text-primary)', fontSize: 13 };
3166
+ var fieldGroupStyle = { marginBottom: 16 };
3167
+ var rowStyle = { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 };
3168
+
3169
+ // Other agents this agent could report to (exclude self)
3170
+ var otherAgents = allAgents.filter(function(a) { return a.id !== agentId; });
3171
+
3172
+ if (editing) {
3173
+ return h(Fragment, null,
3174
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
3175
+ h('h3', { style: { margin: 0, fontSize: 16, fontWeight: 600 } }, 'Edit Manager & Daily Catch-Up'),
3176
+ h('div', { style: { display: 'flex', gap: 8 } },
3177
+ h('button', { className: 'btn btn-ghost btn-sm', onClick: function() { setEditing(false); } }, 'Cancel'),
3178
+ h('button', { className: 'btn btn-primary btn-sm', disabled: saving, onClick: save }, saving ? 'Saving...' : 'Save Changes')
3179
+ )
3180
+ ),
3181
+
3182
+ // Manager Card
3183
+ h('div', { className: 'card', style: { padding: 20, marginBottom: 20 } },
3184
+ h('h4', { style: { margin: '0 0 16px', fontSize: 14, fontWeight: 600 } }, 'Manager'),
3185
+ h('p', { style: { fontSize: 13, color: 'var(--text-muted)', marginTop: 0, marginBottom: 16 } }, 'Assign a manager this agent reports to. Can be another agent in the system or an external person (name + email).'),
3186
+
3187
+ h('div', { style: fieldGroupStyle },
3188
+ h('label', { style: labelStyle }, 'Manager Type'),
3189
+ h('select', { style: Object.assign({}, inputStyle, { cursor: 'pointer' }), value: form.managerType, onChange: function(e) {
3190
+ set('managerType', e.target.value);
3191
+ if (e.target.value === 'none') { set('managerAgentId', ''); set('managerName', ''); set('managerEmail', ''); }
3192
+ } },
3193
+ h('option', { value: 'none' }, 'No manager'),
3194
+ h('option', { value: 'internal' }, 'Another agent in this organization'),
3195
+ h('option', { value: 'external' }, 'External person (name + email)')
3196
+ )
3197
+ ),
3198
+
3199
+ form.managerType === 'internal' && h('div', { style: fieldGroupStyle },
3200
+ h('label', { style: labelStyle }, 'Select Agent'),
3201
+ h('select', { style: Object.assign({}, inputStyle, { cursor: 'pointer' }), value: form.managerAgentId, onChange: function(e) { set('managerAgentId', e.target.value); } },
3202
+ h('option', { value: '' }, '-- Select agent --'),
3203
+ otherAgents.map(function(a) {
3204
+ var name = a.config?.identity?.name || a.config?.displayName || a.name || a.id;
3205
+ var role = a.config?.identity?.role || a.config?.role || '';
3206
+ return h('option', { key: a.id, value: a.id }, name + (role ? ' (' + role + ')' : ''));
3207
+ })
3208
+ )
3209
+ ),
3210
+
3211
+ form.managerType === 'external' && h(Fragment, null,
3212
+ h('div', { style: rowStyle },
3213
+ h('div', { style: fieldGroupStyle },
3214
+ h('label', { style: labelStyle }, 'Manager Name'),
3215
+ h('input', { style: inputStyle, type: 'text', value: form.managerName, placeholder: 'e.g. Sarah Johnson', onChange: function(e) { set('managerName', e.target.value); } })
3216
+ ),
3217
+ h('div', { style: fieldGroupStyle },
3218
+ h('label', { style: labelStyle }, 'Manager Email'),
3219
+ h('input', { style: inputStyle, type: 'email', value: form.managerEmail, placeholder: 'e.g. sarah@company.com', onChange: function(e) { set('managerEmail', e.target.value); } })
3220
+ )
3221
+ ),
3222
+ h('p', { style: { fontSize: 12, color: 'var(--text-muted)', marginTop: 0, marginBottom: 0 } }, 'The agent will email this person for daily catch-ups, status reports, and escalations.')
3223
+ )
3224
+ ),
3225
+
3226
+ // Daily Catch-Up Card
3227
+ h('div', { className: 'card', style: { padding: 20 } },
3228
+ h('h4', { style: { margin: '0 0 16px', fontSize: 14, fontWeight: 600 } }, 'Daily Catch-Up'),
3229
+ h('p', { style: { fontSize: 13, color: 'var(--text-muted)', marginTop: 0, marginBottom: 16 } }, 'When enabled, the agent sends a daily status email to its manager with goals, progress, and blockers.'),
3230
+
3231
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 10, marginBottom: 16 } },
3232
+ h('label', { style: { display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: 13, fontWeight: 600 } },
3233
+ h('input', { type: 'checkbox', checked: form.catchUpEnabled, onChange: function(e) { set('catchUpEnabled', e.target.checked); } }),
3234
+ 'Enable daily catch-up'
3235
+ )
3236
+ ),
3237
+
3238
+ form.catchUpEnabled && h('div', { style: rowStyle },
3239
+ h('div', { style: fieldGroupStyle },
3240
+ h('label', { style: labelStyle }, 'Time'),
3241
+ h('input', { style: inputStyle, type: 'time', value: form.catchUpTime, onChange: function(e) { set('catchUpTime', e.target.value); } })
3242
+ ),
3243
+ h('div', { style: fieldGroupStyle },
3244
+ h('label', { style: labelStyle }, 'Timezone'),
3245
+ h('select', { style: Object.assign({}, inputStyle, { cursor: 'pointer' }), value: form.catchUpTimezone, onChange: function(e) { set('catchUpTimezone', e.target.value); } },
3246
+ COMMON_TIMEZONES.map(function(tz) { return h('option', { key: tz, value: tz }, tz.replace(/_/g, ' ')); })
3247
+ )
3248
+ )
3249
+ ),
3250
+
3251
+ form.catchUpEnabled && !form.managerType !== 'none' && form.managerType === 'none' && h('div', {
3252
+ style: { padding: '10px 14px', background: 'var(--warning-soft, #fff3cd)', borderRadius: 6, fontSize: 13, color: 'var(--warning-text, #856404)', marginTop: 12 }
3253
+ }, 'Note: Catch-up is enabled but no manager is assigned. The agent won\'t have anyone to report to.')
3254
+ )
3255
+ );
3256
+ }
3257
+
3258
+ // View mode
3259
+ var catchUpEnabled = catchUp.enabled || catchUp.time;
3260
+ var catchUpTime = catchUp.time || '09:00';
3261
+ var catchUpTz = catchUp.timezone || 'America/New_York';
3262
+
3263
+ return h(Fragment, null,
3264
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
3265
+ h('h3', { style: { margin: 0, fontSize: 16, fontWeight: 600 } }, 'Manager & Daily Catch-Up'),
3266
+ h('button', { className: 'btn btn-primary btn-sm', onClick: startEdit }, I.journal(), ' Edit')
3267
+ ),
3268
+
3269
+ // Manager Card
3270
+ h('div', { className: 'card', style: { padding: 20, marginBottom: 20 } },
3271
+ h('h4', { style: { margin: '0 0 16px', fontSize: 14, fontWeight: 600 } }, 'Reports To'),
3272
+ resolved
3273
+ ? h('div', { style: { display: 'flex', alignItems: 'center', gap: 12 } },
3274
+ h('div', { style: {
3275
+ width: 40, height: 40, borderRadius: '50%', background: resolved.type === 'external' ? 'var(--accent)' : 'var(--primary)',
3276
+ display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 16, fontWeight: 700, color: '#fff', flexShrink: 0
3277
+ } }, (resolved.name || '?').charAt(0).toUpperCase()),
3278
+ h('div', null,
3279
+ h('div', { style: { fontSize: 14, fontWeight: 600 } }, resolved.name),
3280
+ resolved.type === 'external'
3281
+ ? h('div', { style: { fontSize: 13, color: 'var(--text-muted)', fontFamily: 'var(--font-mono, monospace)' } }, resolved.email)
3282
+ : h('span', { className: 'badge badge-neutral', style: { fontSize: 11 } }, 'Internal Agent')
3283
+ )
3284
+ )
3285
+ : h('div', { style: { fontSize: 14, color: 'var(--text-muted)' } }, 'No manager assigned')
3286
+ ),
3287
+
3288
+ // Daily Catch-Up Card
3289
+ h('div', { className: 'card', style: { padding: 20 } },
3290
+ h('h4', { style: { margin: '0 0 16px', fontSize: 14, fontWeight: 600 } }, 'Daily Catch-Up'),
3291
+ catchUpEnabled
3292
+ ? h('div', null,
3293
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 } },
3294
+ h('span', { style: { width: 8, height: 8, borderRadius: '50%', background: 'var(--success)', display: 'inline-block' } }),
3295
+ h('span', { style: { fontSize: 14, fontWeight: 600 } }, 'Active')
3296
+ ),
3297
+ h('div', { style: { fontSize: 13, color: 'var(--text-secondary)' } },
3298
+ 'Sends daily at ', h('strong', null, catchUpTime), ' ', catchUpTz.replace(/_/g, ' ')
3299
+ ),
3300
+ !resolved && h('div', { style: { fontSize: 12, color: 'var(--warning-text, #856404)', marginTop: 8 } }, 'Warning: No manager assigned — catch-up emails have no recipient.')
3301
+ )
3302
+ : h('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
3303
+ h('span', { style: { width: 8, height: 8, borderRadius: '50%', background: 'var(--text-muted)', display: 'inline-block' } }),
3304
+ h('span', { style: { fontSize: 14, color: 'var(--text-muted)' } }, 'Not configured')
3305
+ )
3306
+ )
3307
+ );
3308
+ }
3309
+
3041
3310
  // ════════════════════════════════════════════════════════════
3042
3311
  // SKILLS SECTION — View and manage agent skills
3043
3312
  // ════════════════════════════════════════════════════════════
@@ -3897,8 +4166,8 @@ function AgentDetailPage(props) {
3897
4166
  var _agents = useState([]);
3898
4167
  var agents = _agents[0]; var setAgents = _agents[1];
3899
4168
 
3900
- var TABS = ['overview', 'personal', 'configuration', 'skills', 'permissions', 'activity', 'communication', 'workforce', 'memory', 'guardrails', 'budget', 'tool-security', 'deployment'];
3901
- var TAB_LABELS = { 'tool-security': 'Tool Security' };
4169
+ var TABS = ['overview', 'personal', 'configuration', 'manager', 'skills', 'permissions', 'activity', 'communication', 'workforce', 'memory', 'guardrails', 'budget', 'tool-security', 'deployment'];
4170
+ var TAB_LABELS = { 'tool-security': 'Tool Security', 'manager': 'Manager & Catch-Up' };
3902
4171
 
3903
4172
  var load = function() {
3904
4173
  setLoading(true);
@@ -4020,6 +4289,7 @@ function AgentDetailPage(props) {
4020
4289
  tab === 'overview' && h(OverviewSection, { agentId: agentId, agent: agent, engineAgent: engineAgent, profile: profile, reload: load, agents: agents, onBack: onBack }),
4021
4290
  tab === 'personal' && h(PersonalDetailsSection, { agentId: agentId, agent: agent, engineAgent: engineAgent, reload: load }),
4022
4291
  tab === 'configuration' && h(ConfigurationSection, { agentId: agentId, engineAgent: engineAgent, reload: load }),
4292
+ tab === 'manager' && h(ManagerCatchUpSection, { agentId: agentId, engineAgent: engineAgent, agents: agents, reload: load }),
4023
4293
  tab === 'skills' && h(SkillsSection, { agentId: agentId, engineAgent: engineAgent, reload: load }),
4024
4294
  tab === 'permissions' && h(PermissionsSection, { agentId: agentId, profile: profile, reload: load }),
4025
4295
  tab === 'activity' && h(ActivitySection, { agentId: agentId }),
@@ -211,6 +211,18 @@ export function SkillsPage() {
211
211
  setConfigSaving(false);
212
212
  };
213
213
 
214
+ // Install a builtin skill
215
+ var installBuiltinSkill = async function(skillId) {
216
+ try {
217
+ await engineCall('/community/skills/' + skillId + '/install', {
218
+ method: 'POST',
219
+ body: JSON.stringify({ orgId: getOrgId() })
220
+ });
221
+ toast('Skill installed', 'success');
222
+ loadInstalled();
223
+ } catch (e) { toast(e.message || 'Install failed', 'error'); }
224
+ };
225
+
214
226
  // Computed
215
227
  var allSkills = Object.entries(skills).flatMap(function(entry) {
216
228
  return entry[1].map(function(s) { return Object.assign({}, s, { category: entry[0] }); });
@@ -246,11 +258,26 @@ export function SkillsPage() {
246
258
  return h('div', { key: cat, style: { marginBottom: 24 } },
247
259
  h('h3', { style: { fontSize: 13, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)', marginBottom: 10 } }, cat.replace(/-/g, ' ')),
248
260
  h('div', { className: 'skill-grid' }, list.map(function(s) {
261
+ var isInstalled = installed.some(function(i) { return i.skillId === s.id; });
249
262
  return h('div', { key: s.id, className: 'skill-card' },
250
263
  h('div', { className: 'skill-cat' }, s.category || cat),
251
264
  h('div', { className: 'skill-name' }, s.name),
252
265
  h('div', { className: 'skill-desc' }, s.description),
253
- s.tools && h('div', { style: { marginTop: 6, fontSize: 11, color: 'var(--text-muted)' } }, s.tools.length + ' tools')
266
+ s.tools && h('div', { style: { marginTop: 6, fontSize: 11, color: 'var(--text-muted)' } }, s.tools.length + ' tools'),
267
+ h('div', { style: { marginTop: 8, display: 'flex', gap: 6 } },
268
+ isInstalled
269
+ ? h('span', { style: { fontSize: 11, color: 'var(--success)', fontWeight: 600 } }, '\u2713 Installed')
270
+ : h('button', {
271
+ className: 'btn btn-primary btn-sm',
272
+ style: { fontSize: 11, padding: '3px 10px' },
273
+ onClick: function() { installBuiltinSkill(s.id); }
274
+ }, 'Install'),
275
+ !isInstalled && h('button', {
276
+ className: 'btn btn-secondary btn-sm',
277
+ style: { fontSize: 11, padding: '3px 10px' },
278
+ onClick: function() { setTokenModal({ skillId: s.id, skillName: s.name }); }
279
+ }, 'Add Token')
280
+ )
254
281
  );
255
282
  }))
256
283
  );