@agenticmail/enterprise 0.5.76 → 0.5.78

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.
@@ -5,6 +5,7 @@
5
5
  * Requires agent to have Google OAuth configured with appropriate scopes.
6
6
  */
7
7
 
8
+ export { createGmailTools } from './gmail.js';
8
9
  export { createGoogleCalendarTools } from './calendar.js';
9
10
  export { createGoogleDriveTools } from './drive.js';
10
11
  export { createGoogleSheetsTools } from './sheets.js';
@@ -13,6 +14,7 @@ export { createGoogleContactsTools } from './contacts.js';
13
14
 
14
15
  import type { AnyAgentTool, ToolCreationOptions } from '../../types.js';
15
16
  import type { TokenProvider } from '../oauth-token-provider.js';
17
+ import { createGmailTools } from './gmail.js';
16
18
  import { createGoogleCalendarTools } from './calendar.js';
17
19
  import { createGoogleDriveTools } from './drive.js';
18
20
  import { createGoogleSheetsTools } from './sheets.js';
@@ -27,8 +29,13 @@ export interface GoogleToolsConfig {
27
29
  * Create all Google Workspace tools for an agent.
28
30
  * Returns ~30 tools covering Calendar, Drive, Sheets, Docs, and Contacts.
29
31
  */
32
+ /**
33
+ * Create all Google Workspace tools for an agent.
34
+ * Returns ~40 tools covering Gmail, Calendar, Drive, Sheets, Docs, and Contacts.
35
+ */
30
36
  export function createAllGoogleTools(config: GoogleToolsConfig, options?: ToolCreationOptions): AnyAgentTool[] {
31
37
  return [
38
+ ...createGmailTools(config, options),
32
39
  ...createGoogleCalendarTools(config, options),
33
40
  ...createGoogleDriveTools(config, options),
34
41
  ...createGoogleSheetsTools(config, options),
@@ -3802,6 +3802,182 @@ var _tsToggleRow = { display: 'flex', alignItems: 'center', justifyContent: 'spa
3802
3802
  var _tsGrid = { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 };
3803
3803
  // --- EmailSection -------------------------------------------------------
3804
3804
 
3805
+ // ════════════════════════════════════════════════════════════
3806
+ // TOOLS SECTION — Toggle tool categories per agent
3807
+ // ════════════════════════════════════════════════════════════
3808
+
3809
+ function ToolsSection(props) {
3810
+ var agentId = props.agentId;
3811
+ var _d = useApp(); var toast = _d.toast;
3812
+ var _loading = useState(true); var loading = _loading[0]; var setLoading = _loading[1];
3813
+ var _cats = useState([]); var cats = _cats[0]; var setCats = _cats[1];
3814
+ var _stats = useState({}); var stats = _stats[0]; var setStats = _stats[1];
3815
+ var _saving = useState(false); var saving = _saving[0]; var setSaving = _saving[1];
3816
+ var _filter = useState('all'); var filter = _filter[0]; var setFilter = _filter[1];
3817
+ var _expanded = useState(null); var expanded = _expanded[0]; var setExpanded = _expanded[1];
3818
+
3819
+ function load() {
3820
+ setLoading(true);
3821
+ engineCall('/bridge/agents/' + agentId + '/tools')
3822
+ .then(function(d) {
3823
+ setCats(d.categories || []);
3824
+ setStats({ total: d.totalTools, enabled: d.enabledTools });
3825
+ setLoading(false);
3826
+ })
3827
+ .catch(function() { setLoading(false); });
3828
+ }
3829
+
3830
+ useEffect(function() { load(); }, [agentId]);
3831
+
3832
+ function toggle(catId, currentEnabled) {
3833
+ setSaving(true);
3834
+ var body = {};
3835
+ body[catId] = !currentEnabled;
3836
+ engineCall('/bridge/agents/' + agentId + '/tools', {
3837
+ method: 'PUT',
3838
+ body: JSON.stringify(body),
3839
+ }).then(function() {
3840
+ setCats(function(prev) {
3841
+ return prev.map(function(c) {
3842
+ return c.id === catId ? Object.assign({}, c, { enabled: !currentEnabled }) : c;
3843
+ });
3844
+ });
3845
+ setStats(function(prev) {
3846
+ var cat = cats.find(function(c) { return c.id === catId; });
3847
+ var delta = currentEnabled ? -(cat?.toolCount || 0) : (cat?.toolCount || 0);
3848
+ return Object.assign({}, prev, { enabled: (prev.enabled || 0) + delta });
3849
+ });
3850
+ toast((!currentEnabled ? 'Enabled' : 'Disabled') + ' tools', 'success');
3851
+ setSaving(false);
3852
+ }).catch(function(e) { toast(e.message, 'error'); setSaving(false); });
3853
+ }
3854
+
3855
+ function toggleAll(enable) {
3856
+ setSaving(true);
3857
+ var body = {};
3858
+ cats.forEach(function(c) { if (!c.alwaysOn) body[c.id] = enable; });
3859
+ engineCall('/bridge/agents/' + agentId + '/tools', {
3860
+ method: 'PUT',
3861
+ body: JSON.stringify(body),
3862
+ }).then(function() {
3863
+ load();
3864
+ toast(enable ? 'All tools enabled' : 'All optional tools disabled', 'success');
3865
+ setSaving(false);
3866
+ }).catch(function(e) { toast(e.message, 'error'); setSaving(false); });
3867
+ }
3868
+
3869
+ if (loading) return h('div', { className: 'card', style: { padding: 40, textAlign: 'center' } }, 'Loading tools...');
3870
+
3871
+ var filtered = cats.filter(function(c) {
3872
+ if (filter === 'enabled') return c.enabled;
3873
+ if (filter === 'disabled') return !c.enabled;
3874
+ if (filter === 'google') return c.requiresOAuth === 'google';
3875
+ if (filter === 'enterprise') return c.id.startsWith('enterprise_');
3876
+ return true;
3877
+ });
3878
+
3879
+ var googleCats = cats.filter(function(c) { return c.requiresOAuth === 'google'; });
3880
+ var googleAvailable = googleCats.some(function(c) { return c.isAvailable; });
3881
+
3882
+ return h('div', null,
3883
+ // Stats bar
3884
+ h('div', { style: { display: 'flex', gap: 16, marginBottom: 16, flexWrap: 'wrap', alignItems: 'center' } },
3885
+ h('div', { className: 'card', style: { padding: '12px 16px', flex: '1 1 auto', minWidth: 150 } },
3886
+ h('div', { style: { fontSize: 24, fontWeight: 700, color: 'var(--accent)' } }, stats.enabled || 0),
3887
+ h('div', { style: { fontSize: 12, color: 'var(--text-muted)' } }, 'of ' + (stats.total || 0) + ' tools enabled')
3888
+ ),
3889
+ h('div', { style: { display: 'flex', gap: 8 } },
3890
+ h('button', { className: 'btn btn-sm', disabled: saving, onClick: function() { toggleAll(true); } }, 'Enable All'),
3891
+ h('button', { className: 'btn btn-sm btn-danger', disabled: saving, onClick: function() { toggleAll(false); } }, 'Disable Optional')
3892
+ )
3893
+ ),
3894
+
3895
+ // Google Workspace notice
3896
+ !googleAvailable && googleCats.length > 0 && h('div', { style: { padding: '12px 16px', background: 'var(--warning-soft)', borderRadius: 'var(--radius)', marginBottom: 16, fontSize: 12 } },
3897
+ h('strong', null, '\u26A0\uFE0F Google Workspace tools require OAuth'), ' — ',
3898
+ 'Connect a Google account in the ', h('strong', null, 'Email'), ' tab to unlock Gmail, Calendar, Drive, Sheets, Docs, and Contacts tools.'
3899
+ ),
3900
+
3901
+ // Filter tabs
3902
+ h('div', { className: 'tabs', style: { marginBottom: 16 } },
3903
+ [
3904
+ { id: 'all', label: 'All' },
3905
+ { id: 'enabled', label: 'Enabled' },
3906
+ { id: 'disabled', label: 'Disabled' },
3907
+ { id: 'google', label: 'Google Workspace' },
3908
+ { id: 'enterprise', label: 'Enterprise' },
3909
+ ].map(function(f) {
3910
+ return h('div', { key: f.id, className: 'tab' + (filter === f.id ? ' active' : ''), onClick: function() { setFilter(f.id); } }, f.label);
3911
+ })
3912
+ ),
3913
+
3914
+ // Tool category cards
3915
+ h('div', { style: { display: 'grid', gap: 12 } },
3916
+ filtered.map(function(cat) {
3917
+ var isExpanded = expanded === cat.id;
3918
+ return h('div', {
3919
+ key: cat.id,
3920
+ className: 'card',
3921
+ style: { opacity: !cat.isAvailable && cat.requiresOAuth ? 0.6 : 1 }
3922
+ },
3923
+ h('div', {
3924
+ style: { display: 'flex', alignItems: 'center', gap: 12, padding: '14px 16px', cursor: 'pointer' },
3925
+ onClick: function() { setExpanded(isExpanded ? null : cat.id); }
3926
+ },
3927
+ // Icon
3928
+ h('div', { style: { fontSize: 22, width: 36, textAlign: 'center', flexShrink: 0 } }, cat.icon),
3929
+ // Info
3930
+ h('div', { style: { flex: 1, minWidth: 0 } },
3931
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 2 } },
3932
+ h('span', { style: { fontWeight: 600, fontSize: 14 } }, cat.name),
3933
+ h('span', { className: 'badge', style: { fontSize: 10, padding: '1px 6px', background: 'var(--bg-tertiary)', color: 'var(--text-muted)' } }, cat.toolCount + ' tools'),
3934
+ !cat.isAvailable && cat.requiresOAuth && h('span', { className: 'badge badge-warning', style: { fontSize: 10, padding: '1px 6px' } }, 'OAuth Required'),
3935
+ cat.alwaysOn && h('span', { className: 'badge badge-info', style: { fontSize: 10, padding: '1px 6px' } }, 'Always On')
3936
+ ),
3937
+ h('div', { style: { fontSize: 12, color: 'var(--text-muted)' } }, cat.description)
3938
+ ),
3939
+ // Toggle
3940
+ !cat.alwaysOn && h('div', {
3941
+ onClick: function(e) { e.stopPropagation(); if (!saving && (cat.isAvailable || cat.enabled)) toggle(cat.id, cat.enabled); },
3942
+ style: {
3943
+ width: 44, height: 24, borderRadius: 12, position: 'relative', cursor: saving ? 'not-allowed' : 'pointer',
3944
+ background: cat.enabled ? 'var(--accent)' : 'var(--border)', transition: 'background 0.2s', flexShrink: 0,
3945
+ },
3946
+ },
3947
+ h('div', { style: {
3948
+ width: 20, height: 20, borderRadius: 10, background: '#fff', position: 'absolute', top: 2,
3949
+ left: cat.enabled ? 22 : 2, transition: 'left 0.2s',
3950
+ boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
3951
+ } })
3952
+ ),
3953
+ cat.alwaysOn && h('div', { style: { width: 44, height: 24, flexShrink: 0 } }),
3954
+ // Expand arrow
3955
+ h('div', { style: { fontSize: 12, color: 'var(--text-muted)', marginLeft: 4 } }, isExpanded ? '\u25B2' : '\u25BC')
3956
+ ),
3957
+
3958
+ // Expanded tool list
3959
+ isExpanded && h('div', { style: { borderTop: '1px solid var(--border)', padding: '12px 16px', background: 'var(--bg-secondary)' } },
3960
+ h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 6 } },
3961
+ cat.tools.map(function(t) {
3962
+ return h('span', {
3963
+ key: t,
3964
+ style: {
3965
+ display: 'inline-block', padding: '3px 10px', borderRadius: 4, fontSize: 11,
3966
+ fontFamily: 'var(--font-mono)', background: cat.enabled ? 'var(--accent-soft)' : 'var(--bg-tertiary)',
3967
+ color: cat.enabled ? 'var(--accent)' : 'var(--text-muted)', border: '1px solid ' + (cat.enabled ? 'var(--accent)' : 'var(--border)'),
3968
+ }
3969
+ }, t);
3970
+ })
3971
+ )
3972
+ )
3973
+ );
3974
+ })
3975
+ ),
3976
+
3977
+ filtered.length === 0 && h('div', { style: { textAlign: 'center', padding: 40, color: 'var(--text-muted)' } }, 'No tools match this filter.')
3978
+ );
3979
+ }
3980
+
3805
3981
  function EmailSection(props) {
3806
3982
  var agentId = props.agentId;
3807
3983
  var engineAgent = props.engineAgent || {};
@@ -4588,8 +4764,8 @@ function AgentDetailPage(props) {
4588
4764
  var _agents = useState([]);
4589
4765
  var agents = _agents[0]; var setAgents = _agents[1];
4590
4766
 
4591
- var TABS = ['overview', 'personal', 'email', 'configuration', 'manager', 'skills', 'permissions', 'activity', 'communication', 'workforce', 'memory', 'guardrails', 'budget', 'tool-security', 'deployment'];
4592
- var TAB_LABELS = { 'tool-security': 'Tool Security', 'manager': 'Manager & Catch-Up', 'email': 'Email' };
4767
+ var TABS = ['overview', 'personal', 'email', 'configuration', 'manager', 'tools', 'skills', 'permissions', 'activity', 'communication', 'workforce', 'memory', 'guardrails', 'budget', 'tool-security', 'deployment'];
4768
+ var TAB_LABELS = { 'tool-security': 'Tool Security', 'manager': 'Manager & Catch-Up', 'email': 'Email', 'tools': 'Tools' };
4593
4769
 
4594
4770
  var load = function() {
4595
4771
  setLoading(true);
@@ -4713,6 +4889,7 @@ function AgentDetailPage(props) {
4713
4889
  tab === 'email' && h(EmailSection, { agentId: agentId, engineAgent: engineAgent, reload: load }),
4714
4890
  tab === 'configuration' && h(ConfigurationSection, { agentId: agentId, engineAgent: engineAgent, reload: load }),
4715
4891
  tab === 'manager' && h(ManagerCatchUpSection, { agentId: agentId, engineAgent: engineAgent, agents: agents, reload: load }),
4892
+ tab === 'tools' && h(ToolsSection, { agentId: agentId, engineAgent: engineAgent, reload: load }),
4716
4893
  tab === 'skills' && h(SkillsSection, { agentId: agentId, engineAgent: engineAgent, reload: load }),
4717
4894
  tab === 'permissions' && h(PermissionsSection, { agentId: agentId, profile: profile, reload: load }),
4718
4895
  tab === 'activity' && h(ActivitySection, { agentId: agentId }),
@@ -777,5 +777,205 @@ export function createAgentRoutes(opts: {
777
777
  return c.json({ success: true });
778
778
  });
779
779
 
780
+ // ═══════════════════════════════════════════════════════════
781
+ // TOOL ACCESS CONFIGURATION
782
+ // ═══════════════════════════════════════════════════════════
783
+
784
+ /** Master tool catalog — all available tools grouped by category */
785
+ const TOOL_CATALOG = [
786
+ {
787
+ id: 'core', name: 'Core Tools', description: 'File operations, shell, search, and browser',
788
+ icon: '🔧', alwaysOn: true,
789
+ tools: ['read', 'write', 'edit', 'bash', 'glob', 'grep', 'web_fetch', 'web_search', 'browser', 'memory'],
790
+ },
791
+ {
792
+ id: 'agenticmail', name: 'AgenticMail', description: 'Email send/receive, inbox management, inter-agent messaging',
793
+ icon: '📧',
794
+ tools: ['agenticmail_inbox', 'agenticmail_read', 'agenticmail_send', 'agenticmail_reply', 'agenticmail_forward',
795
+ 'agenticmail_search', 'agenticmail_labels', 'agenticmail_folders', 'agenticmail_drafts',
796
+ 'agenticmail_move', 'agenticmail_delete', 'agenticmail_batch_read', 'agenticmail_batch_delete',
797
+ 'agenticmail_contacts', 'agenticmail_templates', 'agenticmail_message_agent', 'agenticmail_call_agent',
798
+ 'agenticmail_check_tasks', 'agenticmail_complete_task', 'agenticmail_identity'],
799
+ },
800
+ {
801
+ id: 'gmail', name: 'Gmail', description: 'Native Gmail API — search, send, reply, labels, drafts, threads, attachments',
802
+ icon: '✉️', requiresOAuth: 'google',
803
+ tools: ['gmail_search', 'gmail_read', 'gmail_thread', 'gmail_send', 'gmail_reply', 'gmail_forward',
804
+ 'gmail_modify', 'gmail_trash', 'gmail_labels', 'gmail_drafts', 'gmail_attachment', 'gmail_profile', 'gmail_vacation'],
805
+ },
806
+ {
807
+ id: 'google_calendar', name: 'Google Calendar', description: 'Event management, scheduling, free/busy lookup',
808
+ icon: '📅', requiresOAuth: 'google',
809
+ tools: ['google_calendar_list', 'google_calendar_events', 'google_calendar_create_event',
810
+ 'google_calendar_update_event', 'google_calendar_delete_event', 'google_calendar_freebusy'],
811
+ },
812
+ {
813
+ id: 'google_drive', name: 'Google Drive', description: 'File management, search, sharing, content export',
814
+ icon: '📁', requiresOAuth: 'google',
815
+ tools: ['google_drive_list', 'google_drive_get', 'google_drive_create', 'google_drive_delete',
816
+ 'google_drive_share', 'google_drive_move'],
817
+ },
818
+ {
819
+ id: 'google_sheets', name: 'Google Sheets', description: 'Spreadsheet read/write, cell operations, formulas',
820
+ icon: '📊', requiresOAuth: 'google',
821
+ tools: ['google_sheets_get', 'google_sheets_read', 'google_sheets_write', 'google_sheets_append',
822
+ 'google_sheets_clear', 'google_sheets_create', 'google_sheets_add_sheet'],
823
+ },
824
+ {
825
+ id: 'google_docs', name: 'Google Docs', description: 'Document read/write, text insert, find & replace',
826
+ icon: '📝', requiresOAuth: 'google',
827
+ tools: ['google_docs_read', 'google_docs_create', 'google_docs_write'],
828
+ },
829
+ {
830
+ id: 'google_contacts', name: 'Google Contacts', description: 'Contact search, directory lookup, CRUD',
831
+ icon: '👥', requiresOAuth: 'google',
832
+ tools: ['google_contacts_list', 'google_contacts_search', 'google_contacts_search_directory',
833
+ 'google_contacts_create', 'google_contacts_update'],
834
+ },
835
+ {
836
+ id: 'enterprise_database', name: 'Database', description: 'SQL queries, schema inspection, data sampling',
837
+ icon: '🗄️',
838
+ tools: ['enterprise_sql_query', 'enterprise_sql_schema', 'enterprise_sql_explain',
839
+ 'enterprise_sql_tables', 'enterprise_sql_sample', 'enterprise_sql_write'],
840
+ },
841
+ {
842
+ id: 'enterprise_spreadsheet', name: 'Spreadsheet', description: 'CSV/Excel read, write, filter, aggregate, transform, pivot',
843
+ icon: '📈',
844
+ tools: ['enterprise_csv_read', 'enterprise_csv_write', 'enterprise_csv_filter', 'enterprise_csv_aggregate',
845
+ 'enterprise_csv_transform', 'enterprise_csv_merge', 'enterprise_csv_pivot', 'enterprise_csv_convert'],
846
+ },
847
+ {
848
+ id: 'enterprise_documents', name: 'Documents', description: 'PDF/DOCX generation, OCR, format conversion',
849
+ icon: '📄',
850
+ tools: ['enterprise_pdf_generate', 'enterprise_docx_generate', 'enterprise_ocr', 'enterprise_invoice_parse',
851
+ 'enterprise_doc_convert', 'enterprise_doc_merge', 'enterprise_doc_extract', 'enterprise_doc_sign'],
852
+ },
853
+ {
854
+ id: 'enterprise_http', name: 'HTTP Client', description: 'HTTP requests, GraphQL, batch calls, downloads',
855
+ icon: '🌐',
856
+ tools: ['enterprise_http_request', 'enterprise_http_graphql', 'enterprise_http_batch', 'enterprise_http_download'],
857
+ },
858
+ {
859
+ id: 'enterprise_workflow', name: 'Workflow', description: 'Approval requests, status tracking, escalation',
860
+ icon: '🔄',
861
+ tools: ['enterprise_approval_request', 'enterprise_approval_status', 'enterprise_approval_pending',
862
+ 'enterprise_approval_cancel', 'enterprise_approval_remind'],
863
+ },
864
+ {
865
+ id: 'enterprise_notifications', name: 'Notifications', description: 'Send alerts, broadcast, webhook, escalate',
866
+ icon: '🔔',
867
+ tools: ['enterprise_notify_send', 'enterprise_notify_broadcast', 'enterprise_notify_webhook',
868
+ 'enterprise_notify_escalate', 'enterprise_notify_schedule'],
869
+ },
870
+ {
871
+ id: 'enterprise_finance', name: 'Finance', description: 'Budget queries, expenses, spending reports, forecasts',
872
+ icon: '💰',
873
+ tools: ['enterprise_budget_query', 'enterprise_expense_log', 'enterprise_spending_summary',
874
+ 'enterprise_invoice_list', 'enterprise_budget_forecast'],
875
+ },
876
+ {
877
+ id: 'enterprise_security', name: 'Security Scanning', description: 'Secret scanning, PII detection, dependency audit',
878
+ icon: '🔒',
879
+ tools: ['enterprise_secret_scan', 'enterprise_pii_scan', 'enterprise_pii_redact',
880
+ 'enterprise_dep_audit', 'enterprise_compliance_check', 'enterprise_hash'],
881
+ },
882
+ {
883
+ id: 'enterprise_code', name: 'Code Sandbox', description: 'Run JavaScript, Python, shell scripts, JSON transforms',
884
+ icon: '💻',
885
+ tools: ['enterprise_run_js', 'enterprise_run_python', 'enterprise_run_shell',
886
+ 'enterprise_json_transform', 'enterprise_regex'],
887
+ },
888
+ {
889
+ id: 'enterprise_translation', name: 'Translation', description: 'Text and document translation, language detection',
890
+ icon: '🌍',
891
+ tools: ['enterprise_translate', 'enterprise_translate_doc', 'enterprise_translate_batch',
892
+ 'enterprise_detect_language', 'enterprise_translate_glossary'],
893
+ },
894
+ {
895
+ id: 'enterprise_vision', name: 'Vision', description: 'Image analysis, OCR, UI analysis, chart extraction',
896
+ icon: '👁️',
897
+ tools: ['enterprise_image_describe', 'enterprise_image_ocr', 'enterprise_ui_analyze',
898
+ 'enterprise_chart_extract', 'enterprise_image_compare'],
899
+ },
900
+ {
901
+ id: 'enterprise_knowledge', name: 'Knowledge Base', description: 'Internal knowledge search, spaces, updates',
902
+ icon: '📚',
903
+ tools: ['enterprise_kb_search', 'enterprise_kb_spaces', 'enterprise_kb_recent',
904
+ 'enterprise_kb_article', 'enterprise_kb_contribute'],
905
+ },
906
+ {
907
+ id: 'enterprise_logs', name: 'Logs', description: 'Log search, aggregation, tail, error correlation',
908
+ icon: '📋',
909
+ tools: ['enterprise_log_search', 'enterprise_log_aggregate', 'enterprise_log_tail',
910
+ 'enterprise_log_correlate', 'enterprise_log_errors'],
911
+ },
912
+ {
913
+ id: 'enterprise_diff', name: 'Diff', description: 'Text, JSON, and spreadsheet comparison',
914
+ icon: '↔️',
915
+ tools: ['enterprise_text_diff', 'enterprise_json_diff', 'enterprise_spreadsheet_diff', 'enterprise_diff_summary'],
916
+ },
917
+ ];
918
+
919
+ /**
920
+ * GET /bridge/agents/:id/tools — List available tool categories with enabled/disabled status
921
+ */
922
+ router.get('/bridge/agents/:id/tools', (c) => {
923
+ const agentId = c.req.param('id');
924
+ const managed = lifecycle.getAgent(agentId);
925
+ if (!managed) return c.json({ error: 'Agent not found' }, 404);
926
+
927
+ const toolConfig = managed.config?.toolAccess || {};
928
+ const emailConfig = managed.config?.emailConfig;
929
+ const hasGoogleOAuth = emailConfig?.oauthProvider === 'google' && emailConfig?.oauthAccessToken;
930
+ const hasMicrosoftOAuth = emailConfig?.oauthProvider === 'microsoft' && emailConfig?.oauthAccessToken;
931
+
932
+ const categories = TOOL_CATALOG.map(cat => {
933
+ const isAvailable = !cat.requiresOAuth
934
+ || (cat.requiresOAuth === 'google' && hasGoogleOAuth)
935
+ || (cat.requiresOAuth === 'microsoft' && hasMicrosoftOAuth);
936
+
937
+ // Default: core is always on, others on if available
938
+ const defaultEnabled = cat.alwaysOn || isAvailable;
939
+ const enabled = cat.alwaysOn ? true : (toolConfig[cat.id] !== undefined ? toolConfig[cat.id] : defaultEnabled);
940
+
941
+ return {
942
+ id: cat.id, name: cat.name, description: cat.description, icon: cat.icon,
943
+ toolCount: cat.tools.length, tools: cat.tools,
944
+ enabled, isAvailable, alwaysOn: cat.alwaysOn || false,
945
+ requiresOAuth: cat.requiresOAuth || null,
946
+ };
947
+ });
948
+
949
+ const totalTools = categories.reduce((s, c) => s + c.toolCount, 0);
950
+ const enabledTools = categories.filter(c => c.enabled).reduce((s, c) => s + c.toolCount, 0);
951
+
952
+ return c.json({ categories, totalTools, enabledTools });
953
+ });
954
+
955
+ /**
956
+ * PUT /bridge/agents/:id/tools — Update tool access configuration
957
+ * Body: { [categoryId]: boolean }
958
+ */
959
+ router.put('/bridge/agents/:id/tools', async (c) => {
960
+ const agentId = c.req.param('id');
961
+ const managed = lifecycle.getAgent(agentId);
962
+ if (!managed) return c.json({ error: 'Agent not found' }, 404);
963
+
964
+ const body = await c.req.json();
965
+ if (!managed.config) managed.config = {};
966
+ if (!managed.config.toolAccess) managed.config.toolAccess = {};
967
+
968
+ for (const [catId, enabled] of Object.entries(body)) {
969
+ // Don't allow disabling core tools
970
+ const cat = TOOL_CATALOG.find(c => c.id === catId);
971
+ if (cat?.alwaysOn) continue;
972
+ managed.config.toolAccess[catId] = !!enabled;
973
+ }
974
+
975
+ managed.updatedAt = new Date().toISOString();
976
+ await lifecycle.saveAgent(agentId);
977
+ return c.json({ success: true, toolAccess: managed.config.toolAccess });
978
+ });
979
+
780
980
  return router;
781
981
  }