@agenticmail/enterprise 0.5.301 → 0.5.303
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.
- package/dist/chunk-4VGAZULN.js +1519 -0
- package/dist/chunk-CRXYUYVJ.js +4395 -0
- package/dist/chunk-QCGSFWKU.js +48 -0
- package/dist/cli-agent-YSQVDBOZ.js +1778 -0
- package/dist/cli-recover-2LWWVD4Q.js +487 -0
- package/dist/cli-serve-JZEXPYXY.js +143 -0
- package/dist/cli-verify-TZMX3GWV.js +149 -0
- package/dist/cli.js +5 -5
- package/dist/dashboard/app.js +57 -1
- package/dist/dashboard/components/org-switcher.js +118 -0
- package/dist/dashboard/pages/agents.js +8 -1
- package/dist/dashboard/pages/approvals.js +5 -1
- package/dist/dashboard/pages/dashboard.js +6 -2
- package/dist/dashboard/pages/guardrails.js +20 -16
- package/dist/dashboard/pages/journal.js +6 -2
- package/dist/dashboard/pages/knowledge-contributions.js +18 -10
- package/dist/dashboard/pages/knowledge.js +32 -9
- package/dist/dashboard/pages/messages.js +8 -4
- package/dist/dashboard/pages/org-chart.js +5 -1
- package/dist/dashboard/pages/organizations.js +39 -3
- package/dist/dashboard/pages/skills.js +15 -11
- package/dist/dashboard/pages/task-pipeline.js +6 -2
- package/dist/dashboard/pages/users.js +34 -4
- package/dist/dashboard/pages/workforce.js +5 -1
- package/dist/factory-QYGGXVYW.js +9 -0
- package/dist/index.js +3 -3
- package/dist/postgres-ALNOGUUM.js +819 -0
- package/dist/server-3R5KZPLA.js +15 -0
- package/dist/setup-ZZAOBEY4.js +20 -0
- package/dist/sqlite-2QPVZQ27.js +566 -0
- package/package.json +1 -1
- package/src/admin/routes.ts +32 -1
- package/src/auth/routes.ts +36 -2
- package/src/dashboard/app.js +57 -1
- package/src/dashboard/components/org-switcher.js +118 -0
- package/src/dashboard/pages/agents.js +8 -1
- package/src/dashboard/pages/approvals.js +5 -1
- package/src/dashboard/pages/dashboard.js +6 -2
- package/src/dashboard/pages/guardrails.js +20 -16
- package/src/dashboard/pages/journal.js +6 -2
- package/src/dashboard/pages/knowledge-contributions.js +18 -10
- package/src/dashboard/pages/knowledge.js +32 -9
- package/src/dashboard/pages/messages.js +8 -4
- package/src/dashboard/pages/org-chart.js +5 -1
- package/src/dashboard/pages/organizations.js +39 -3
- package/src/dashboard/pages/skills.js +15 -11
- package/src/dashboard/pages/task-pipeline.js +6 -2
- package/src/dashboard/pages/users.js +34 -4
- package/src/dashboard/pages/workforce.js +5 -1
- package/src/db/adapter.ts +1 -0
- package/src/db/postgres.ts +3 -0
- package/src/db/sqlite.ts +7 -0
package/src/auth/routes.ts
CHANGED
|
@@ -303,7 +303,7 @@ export function createAuthRoutes(
|
|
|
303
303
|
token,
|
|
304
304
|
refreshToken,
|
|
305
305
|
csrf,
|
|
306
|
-
user: { id: user.id, email: user.email, name: user.name, role: user.role, totpEnabled: !!user.totpEnabled },
|
|
306
|
+
user: { id: user.id, email: user.email, name: user.name, role: user.role, totpEnabled: !!user.totpEnabled, clientOrgId: user.clientOrgId || null },
|
|
307
307
|
mustResetPassword: !!user.mustResetPassword,
|
|
308
308
|
});
|
|
309
309
|
});
|
|
@@ -362,7 +362,7 @@ export function createAuthRoutes(
|
|
|
362
362
|
token,
|
|
363
363
|
refreshToken,
|
|
364
364
|
csrf,
|
|
365
|
-
user: { id: user.id, email: user.email, name: user.name, role: user.role, totpEnabled: true },
|
|
365
|
+
user: { id: user.id, email: user.email, name: user.name, role: user.role, totpEnabled: true, clientOrgId: user.clientOrgId || null },
|
|
366
366
|
mustResetPassword: !!user.mustResetPassword,
|
|
367
367
|
...(backupUsed ? { warning: 'Backup code used. You have fewer backup codes remaining.' } : {}),
|
|
368
368
|
});
|
|
@@ -672,6 +672,40 @@ export function createAuthRoutes(
|
|
|
672
672
|
}
|
|
673
673
|
});
|
|
674
674
|
|
|
675
|
+
// ─── Impersonation (owner-only) ──────────────────────────
|
|
676
|
+
|
|
677
|
+
auth.post('/impersonate/:userId', async (c) => {
|
|
678
|
+
// Only owners can impersonate
|
|
679
|
+
const token = await extractToken(c);
|
|
680
|
+
if (!token) return c.json({ error: 'Authentication required' }, 401);
|
|
681
|
+
try {
|
|
682
|
+
const { jwtVerify, SignJWT } = await import('jose');
|
|
683
|
+
const secret = new TextEncoder().encode(jwtSecret);
|
|
684
|
+
const { payload } = await jwtVerify(token, secret);
|
|
685
|
+
const caller = await db.getUser(payload.sub as string);
|
|
686
|
+
if (!caller || caller.role !== 'owner') return c.json({ error: 'Only owners can impersonate users' }, 403);
|
|
687
|
+
|
|
688
|
+
const targetId = c.req.param('userId');
|
|
689
|
+
const target = await db.getUser(targetId);
|
|
690
|
+
if (!target) return c.json({ error: 'User not found' }, 404);
|
|
691
|
+
|
|
692
|
+
// Generate a short-lived token (1 hour) for the target user with impersonation flag
|
|
693
|
+
const impersonateToken = await new SignJWT({ sub: target.id, role: target.role, impersonatedBy: caller.id })
|
|
694
|
+
.setProtectedHeader({ alg: 'HS256' })
|
|
695
|
+
.setIssuedAt()
|
|
696
|
+
.setExpirationTime('1h')
|
|
697
|
+
.sign(secret);
|
|
698
|
+
|
|
699
|
+
return c.json({
|
|
700
|
+
token: impersonateToken,
|
|
701
|
+
user: { id: target.id, email: target.email, name: target.name, role: target.role, totpEnabled: !!target.totpEnabled, clientOrgId: target.clientOrgId || null, permissions: target.permissions },
|
|
702
|
+
impersonatedBy: { id: caller.id, name: caller.name, email: caller.email },
|
|
703
|
+
});
|
|
704
|
+
} catch (e: any) {
|
|
705
|
+
return c.json({ error: e.message || 'Impersonation failed' }, 500);
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
|
|
675
709
|
// ─── Logout ─────────────────────────────────────────────
|
|
676
710
|
|
|
677
711
|
auth.post('/logout', (c) => {
|
package/src/dashboard/app.js
CHANGED
|
@@ -97,6 +97,7 @@ function App() {
|
|
|
97
97
|
const [permissions, setPermissions] = useState('*'); // '*' = full access, or { pageId: true | ['tab1','tab2'] }
|
|
98
98
|
const [mustResetPassword, setMustResetPassword] = useState(false);
|
|
99
99
|
const [show2faReminder, setShow2faReminder] = useState(false);
|
|
100
|
+
const [impersonating, setImpersonating] = useState(null); // { user, impersonatedBy }
|
|
100
101
|
const [forceResetPw, setForceResetPw] = useState('');
|
|
101
102
|
const [forceResetPw2, setForceResetPw2] = useState('');
|
|
102
103
|
const [forceResetLoading, setForceResetLoading] = useState(false);
|
|
@@ -146,6 +147,12 @@ function App() {
|
|
|
146
147
|
apiCall('/settings').then(d => { const s = d.settings || d || {}; if (s.primaryColor) applyBrandColor(s.primaryColor); if (s.orgId) setOrgId(s.orgId); }).catch(() => {});
|
|
147
148
|
apiCall('/me/permissions').then(d => {
|
|
148
149
|
if (d && d.permissions) setPermissions(d.permissions);
|
|
150
|
+
// If user is assigned to a client org, auto-set org context
|
|
151
|
+
if (d && d.clientOrgId) {
|
|
152
|
+
localStorage.setItem('em_client_org_id', d.clientOrgId);
|
|
153
|
+
} else {
|
|
154
|
+
localStorage.removeItem('em_client_org_id');
|
|
155
|
+
}
|
|
149
156
|
}).catch(() => {});
|
|
150
157
|
}, [authed]);
|
|
151
158
|
|
|
@@ -281,7 +288,44 @@ function App() {
|
|
|
281
288
|
const PageComponent = canAccessPage ? (pages[page] || DashboardPage) : null;
|
|
282
289
|
const sidebarClass = 'sidebar' + (sidebarPinned ? ' expanded' : sidebarHovered ? ' hover-expanded' : '') + (mobileMenuOpen ? ' mobile-open' : '');
|
|
283
290
|
|
|
284
|
-
|
|
291
|
+
// Impersonation functions
|
|
292
|
+
const startImpersonation = useCallback(async (userId) => {
|
|
293
|
+
try {
|
|
294
|
+
const d = await authCall('/impersonate/' + userId, { method: 'POST' });
|
|
295
|
+
if (d.token && d.user) {
|
|
296
|
+
// Store real user info
|
|
297
|
+
setImpersonating({ user: d.user, impersonatedBy: d.impersonatedBy, originalToken: localStorage.getItem('em_token') });
|
|
298
|
+
// Set impersonated user's token
|
|
299
|
+
localStorage.setItem('em_token', d.token);
|
|
300
|
+
setUser(d.user);
|
|
301
|
+
if (d.user.permissions) setPermissions(d.user.permissions);
|
|
302
|
+
if (d.user.clientOrgId) {
|
|
303
|
+
localStorage.setItem('em_client_org_id', d.user.clientOrgId);
|
|
304
|
+
// Fetch org name for display
|
|
305
|
+
apiCall('/organizations/' + d.user.clientOrgId).then(function(o) {
|
|
306
|
+
if (o && o.name) setImpersonating(function(prev) { return prev ? Object.assign({}, prev, { user: Object.assign({}, prev.user, { clientOrgName: o.name }) }) : prev; });
|
|
307
|
+
}).catch(function() {});
|
|
308
|
+
} else localStorage.removeItem('em_client_org_id');
|
|
309
|
+
toast('Now viewing as ' + d.user.name, 'info');
|
|
310
|
+
setPage('dashboard');
|
|
311
|
+
}
|
|
312
|
+
} catch (e) { toast(e.message || 'Impersonation failed', 'error'); }
|
|
313
|
+
}, []);
|
|
314
|
+
|
|
315
|
+
const stopImpersonation = useCallback(() => {
|
|
316
|
+
if (impersonating && impersonating.originalToken) {
|
|
317
|
+
localStorage.setItem('em_token', impersonating.originalToken);
|
|
318
|
+
}
|
|
319
|
+
setImpersonating(null);
|
|
320
|
+
localStorage.removeItem('em_client_org_id');
|
|
321
|
+
// Reload real user
|
|
322
|
+
authCall('/me').then(d => { setUser(d.user || d); }).catch(() => {});
|
|
323
|
+
apiCall('/me/permissions').then(d => { if (d && d.permissions) setPermissions(d.permissions); }).catch(() => {});
|
|
324
|
+
toast('Stopped impersonation', 'success');
|
|
325
|
+
setPage('users');
|
|
326
|
+
}, [impersonating]);
|
|
327
|
+
|
|
328
|
+
return h(AppContext.Provider, { value: { toast, toasts, user, theme, setPage, permissions, impersonating, startImpersonation, stopImpersonation } },
|
|
285
329
|
h('div', { className: 'app-layout' },
|
|
286
330
|
// Mobile hamburger
|
|
287
331
|
h('button', { className: 'mobile-hamburger', onClick: () => setMobileMenuOpen(true) },
|
|
@@ -337,6 +381,18 @@ function App() {
|
|
|
337
381
|
)
|
|
338
382
|
),
|
|
339
383
|
h('div', { className: 'page-content' },
|
|
384
|
+
// Impersonation banner
|
|
385
|
+
impersonating && h('div', { style: { display: 'flex', alignItems: 'center', gap: 12, padding: '10px 16px', margin: '0 0 16px', background: 'rgba(99,102,241,0.12)', border: '2px solid var(--primary, #6366f1)', borderRadius: 8, fontSize: 13 } },
|
|
386
|
+
I.agents(),
|
|
387
|
+
h('div', { style: { flex: 1 } },
|
|
388
|
+
h('strong', null, 'Viewing as: '),
|
|
389
|
+
impersonating.user.name + ' (' + impersonating.user.email + ')',
|
|
390
|
+
impersonating.user.role && h('span', { className: 'badge badge-neutral', style: { marginLeft: 8, fontSize: 10 } }, impersonating.user.role),
|
|
391
|
+
impersonating.user.clientOrgName && h('span', { className: 'badge badge-info', style: { marginLeft: 8, fontSize: 10 } }, 'Org: ' + impersonating.user.clientOrgName),
|
|
392
|
+
impersonating.user.clientOrgId && !impersonating.user.clientOrgName && h('span', { className: 'badge badge-info', style: { marginLeft: 8, fontSize: 10 } }, 'Client Org')
|
|
393
|
+
),
|
|
394
|
+
h('button', { className: 'btn btn-primary btn-sm', onClick: stopImpersonation }, 'Stop Impersonating')
|
|
395
|
+
),
|
|
340
396
|
// 2FA recommendation banner
|
|
341
397
|
show2faReminder && h('div', { style: { display: 'flex', alignItems: 'center', gap: 12, padding: '10px 16px', margin: '0 0 16px', background: 'var(--warning-soft, rgba(245,158,11,0.1))', border: '1px solid var(--warning, #f59e0b)', borderRadius: 8, fontSize: 13 } },
|
|
342
398
|
I.shield(),
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { h, useState, useEffect, Fragment, apiCall, useApp } from './utils.js';
|
|
2
|
+
import { I } from './icons.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* OrgContextSwitcher — Global org context picker for multi-tenant pages.
|
|
6
|
+
*
|
|
7
|
+
* If the current user has a clientOrgId, the switcher is LOCKED to that org
|
|
8
|
+
* (they can only see their org's data). Owners/admins can switch freely.
|
|
9
|
+
*/
|
|
10
|
+
export function OrgContextSwitcher(props) {
|
|
11
|
+
var onOrgChange = props.onOrgChange;
|
|
12
|
+
var selectedOrgId = props.selectedOrgId || '';
|
|
13
|
+
var showLabel = props.showLabel !== false;
|
|
14
|
+
var style = props.style || {};
|
|
15
|
+
|
|
16
|
+
var app = useApp();
|
|
17
|
+
var user = app.user || {};
|
|
18
|
+
var userOrgId = user.clientOrgId || null;
|
|
19
|
+
var isLocked = !!userOrgId && user.role !== 'owner' && user.role !== 'admin';
|
|
20
|
+
|
|
21
|
+
var _orgs = useState([]);
|
|
22
|
+
var orgs = _orgs[0]; var setOrgs = _orgs[1];
|
|
23
|
+
var _loaded = useState(false);
|
|
24
|
+
var loaded = _loaded[0]; var setLoaded = _loaded[1];
|
|
25
|
+
|
|
26
|
+
useEffect(function() {
|
|
27
|
+
apiCall('/organizations').then(function(d) {
|
|
28
|
+
var list = d.organizations || [];
|
|
29
|
+
setOrgs(list);
|
|
30
|
+
setLoaded(true);
|
|
31
|
+
// Auto-select user's org on first load if org-bound
|
|
32
|
+
if (userOrgId && !selectedOrgId) {
|
|
33
|
+
var org = list.find(function(o) { return o.id === userOrgId; });
|
|
34
|
+
if (org) onOrgChange(userOrgId, org);
|
|
35
|
+
}
|
|
36
|
+
}).catch(function() { setLoaded(true); });
|
|
37
|
+
}, [userOrgId]);
|
|
38
|
+
|
|
39
|
+
// Don't render if no client orgs and user isn't org-bound
|
|
40
|
+
if (loaded && orgs.length === 0 && !userOrgId) return null;
|
|
41
|
+
if (!loaded) return null;
|
|
42
|
+
|
|
43
|
+
var effectiveId = isLocked ? userOrgId : selectedOrgId;
|
|
44
|
+
var selectedOrg = orgs.find(function(o) { return o.id === effectiveId; });
|
|
45
|
+
|
|
46
|
+
return h('div', {
|
|
47
|
+
style: Object.assign({
|
|
48
|
+
display: 'flex', alignItems: 'center', gap: 10, padding: '8px 14px',
|
|
49
|
+
background: 'var(--bg-tertiary)', borderRadius: 'var(--radius, 8px)',
|
|
50
|
+
marginBottom: 16, fontSize: 13
|
|
51
|
+
}, style)
|
|
52
|
+
},
|
|
53
|
+
showLabel && h('span', { style: { color: 'var(--text-muted)', fontWeight: 600, whiteSpace: 'nowrap' } }, I.building(), ' Viewing:'),
|
|
54
|
+
isLocked
|
|
55
|
+
? h('div', { style: { fontWeight: 600, fontSize: 13, color: 'var(--text)', display: 'flex', alignItems: 'center', gap: 6 } },
|
|
56
|
+
selectedOrg ? selectedOrg.name : 'Your Organization',
|
|
57
|
+
h('span', { className: 'badge badge-neutral', style: { fontSize: 10 } }, 'Locked')
|
|
58
|
+
)
|
|
59
|
+
: h('select', {
|
|
60
|
+
value: selectedOrgId,
|
|
61
|
+
onChange: function(e) {
|
|
62
|
+
var id = e.target.value;
|
|
63
|
+
var org = orgs.find(function(o) { return o.id === id; });
|
|
64
|
+
onOrgChange(id, org || null);
|
|
65
|
+
},
|
|
66
|
+
style: {
|
|
67
|
+
padding: '6px 10px', borderRadius: 6, border: '1px solid var(--border)',
|
|
68
|
+
background: 'var(--bg-card)', color: 'var(--text)', fontSize: 13,
|
|
69
|
+
cursor: 'pointer', fontWeight: 600, flex: 1, maxWidth: 300
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
h('option', { value: '' }, 'My Organization'),
|
|
73
|
+
orgs.filter(function(o) { return o.is_active !== false; }).map(function(o) {
|
|
74
|
+
return h('option', { key: o.id, value: o.id }, o.name + (o.billing_rate_per_agent > 0 ? ' (' + (o.currency || 'USD') + ' ' + parseFloat(o.billing_rate_per_agent).toFixed(0) + '/agent)' : ''));
|
|
75
|
+
})
|
|
76
|
+
),
|
|
77
|
+
selectedOrg && h('span', { style: { fontSize: 11, color: 'var(--text-muted)' } },
|
|
78
|
+
selectedOrg.contact_name ? selectedOrg.contact_name : '',
|
|
79
|
+
selectedOrg.contact_email ? ' \u2022 ' + selectedOrg.contact_email : ''
|
|
80
|
+
),
|
|
81
|
+
// Impersonation banner
|
|
82
|
+
app.impersonating && h('span', { className: 'badge badge-warning', style: { fontSize: 10, marginLeft: 'auto' } }, 'Impersonating: ' + (user.name || user.email))
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* useOrgContext — Hook that provides org switching state.
|
|
88
|
+
* Auto-selects the user's client org if they are org-bound.
|
|
89
|
+
*/
|
|
90
|
+
export function useOrgContext() {
|
|
91
|
+
var app = useApp();
|
|
92
|
+
var user = app.user || {};
|
|
93
|
+
var userOrgId = user.clientOrgId || '';
|
|
94
|
+
|
|
95
|
+
var _sel = useState(userOrgId);
|
|
96
|
+
var selectedOrgId = _sel[0]; var setSelectedOrgId = _sel[1];
|
|
97
|
+
var _org = useState(null);
|
|
98
|
+
var selectedOrg = _org[0]; var setSelectedOrg = _org[1];
|
|
99
|
+
|
|
100
|
+
// If user changes (e.g. impersonation), update default
|
|
101
|
+
useEffect(function() {
|
|
102
|
+
if (userOrgId && !selectedOrgId) setSelectedOrgId(userOrgId);
|
|
103
|
+
}, [userOrgId]);
|
|
104
|
+
|
|
105
|
+
var onOrgChange = function(id, org) {
|
|
106
|
+
setSelectedOrgId(id);
|
|
107
|
+
setSelectedOrg(org);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
var Switcher = function(extraProps) {
|
|
111
|
+
return h(OrgContextSwitcher, Object.assign({
|
|
112
|
+
selectedOrgId: selectedOrgId,
|
|
113
|
+
onOrgChange: onOrgChange
|
|
114
|
+
}, extraProps || {}));
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
return { selectedOrgId: selectedOrgId, selectedOrg: selectedOrg, onOrgChange: onOrgChange, Switcher: Switcher };
|
|
118
|
+
}
|
|
@@ -3,6 +3,7 @@ import { I } from '../components/icons.js';
|
|
|
3
3
|
import { E } from '../assets/icons/emoji-icons.js';
|
|
4
4
|
import { CULTURES, LANGUAGES, PersonaForm } from '../components/persona-fields.js';
|
|
5
5
|
import { HelpButton } from '../components/help-button.js';
|
|
6
|
+
import { useOrgContext } from '../components/org-switcher.js';
|
|
6
7
|
|
|
7
8
|
// ════════════════════════════════════════════════════════════
|
|
8
9
|
// DEPLOY MODAL
|
|
@@ -1127,6 +1128,7 @@ export function CreateAgentWizard({ onClose, onCreated, toast }) {
|
|
|
1127
1128
|
export function AgentsPage({ onSelectAgent }) {
|
|
1128
1129
|
const app = useApp();
|
|
1129
1130
|
const toast = app.toast;
|
|
1131
|
+
var orgCtx = useOrgContext();
|
|
1130
1132
|
const [agents, setAgents] = useState([]);
|
|
1131
1133
|
const [creating, setCreating] = useState(false);
|
|
1132
1134
|
|
|
@@ -1138,9 +1140,13 @@ export function AgentsPage({ onSelectAgent }) {
|
|
|
1138
1140
|
if (allowedAgents !== '*' && Array.isArray(allowedAgents)) {
|
|
1139
1141
|
all = all.filter(a => allowedAgents.indexOf(a.id) >= 0);
|
|
1140
1142
|
}
|
|
1143
|
+
// Filter by selected org context
|
|
1144
|
+
if (orgCtx.selectedOrgId) {
|
|
1145
|
+
all = all.filter(a => a.client_org_id === orgCtx.selectedOrgId);
|
|
1146
|
+
}
|
|
1141
1147
|
setAgents(all);
|
|
1142
1148
|
}).catch(() => {});
|
|
1143
|
-
useEffect(() => { load(); }, []);
|
|
1149
|
+
useEffect(() => { load(); }, [orgCtx.selectedOrgId]);
|
|
1144
1150
|
|
|
1145
1151
|
// Delete moved to agent detail overview tab with triple confirmation
|
|
1146
1152
|
|
|
@@ -1149,6 +1155,7 @@ export function AgentsPage({ onSelectAgent }) {
|
|
|
1149
1155
|
var _tip = { marginTop: 12, padding: 12, background: 'var(--bg-secondary, #1e293b)', borderRadius: 'var(--radius, 8px)', fontSize: 13 };
|
|
1150
1156
|
|
|
1151
1157
|
return h(Fragment, null,
|
|
1158
|
+
h(orgCtx.Switcher),
|
|
1152
1159
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
|
|
1153
1160
|
h('div', null, h('h1', { style: { fontSize: 20, fontWeight: 700, display: 'flex', alignItems: 'center' } }, 'Agents', h(HelpButton, { label: 'Agents' },
|
|
1154
1161
|
h('p', null, 'Your AI workforce. Each agent has its own email identity, personality, skills, permissions, and deployment target.'),
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { h, useState, useEffect, Fragment, useApp, engineCall, showConfirm, buildAgentEmailMap, buildAgentDataMap, resolveAgentEmail, renderAgentBadge, getOrgId } from '../components/utils.js';
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { HelpButton } from '../components/help-button.js';
|
|
4
|
+
import { useOrgContext } from '../components/org-switcher.js';
|
|
4
5
|
|
|
5
6
|
export function ApprovalsPage() {
|
|
7
|
+
var orgCtx = useOrgContext();
|
|
8
|
+
var effectiveOrgId = orgCtx.selectedOrgId || getOrgId();
|
|
6
9
|
const { toast } = useApp();
|
|
7
10
|
const [pending, setPending] = useState([]);
|
|
8
11
|
const [history, setHistory] = useState([]);
|
|
@@ -13,7 +16,7 @@ export function ApprovalsPage() {
|
|
|
13
16
|
const load = () => {
|
|
14
17
|
engineCall('/approvals/pending').then(d => setPending(d.requests || [])).catch(() => {});
|
|
15
18
|
engineCall('/approvals/history?limit=50').then(d => setHistory(d.requests || [])).catch(() => {});
|
|
16
|
-
engineCall('/agents?orgId=' +
|
|
19
|
+
engineCall('/agents?orgId=' + effectiveOrgId).then(d => setAgents(d.agents || [])).catch(() => {});
|
|
17
20
|
};
|
|
18
21
|
useEffect(() => { load(); }, []);
|
|
19
22
|
|
|
@@ -33,6 +36,7 @@ export function ApprovalsPage() {
|
|
|
33
36
|
var _tip = { marginTop: 12, padding: 12, background: 'var(--bg-secondary, #1e293b)', borderRadius: 'var(--radius, 8px)', fontSize: 13 };
|
|
34
37
|
|
|
35
38
|
return h(Fragment, null,
|
|
39
|
+
h(orgCtx.Switcher),
|
|
36
40
|
h('div', { style: { marginBottom: 20 } },
|
|
37
41
|
h('h1', { style: { fontSize: 20, fontWeight: 700, display: 'flex', alignItems: 'center' } }, 'Approvals', h(HelpButton, { label: 'Approvals' },
|
|
38
42
|
h('p', null, 'The human-in-the-loop checkpoint. When agents attempt sensitive actions (based on your permission settings), they pause and wait for your approval here.'),
|
|
@@ -2,6 +2,7 @@ import { h, useState, useEffect, Fragment, buildAgentEmailMap, buildAgentDataMap
|
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { DetailModal } from '../components/modal.js';
|
|
4
4
|
import { HelpButton } from '../components/help-button.js';
|
|
5
|
+
import { useOrgContext } from '../components/org-switcher.js';
|
|
5
6
|
|
|
6
7
|
export function SetupChecklist({ onNavigate }) {
|
|
7
8
|
const [status, setStatus] = useState(null);
|
|
@@ -46,6 +47,8 @@ export function SetupChecklist({ onNavigate }) {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
export function DashboardPage() {
|
|
50
|
+
var orgCtx = useOrgContext();
|
|
51
|
+
var effectiveOrgId = orgCtx.selectedOrgId || effectiveOrgId;
|
|
49
52
|
const [stats, setStats] = useState(null);
|
|
50
53
|
const [agents, setAgents] = useState([]);
|
|
51
54
|
const [events, setEvents] = useState([]);
|
|
@@ -58,9 +61,9 @@ export function DashboardPage() {
|
|
|
58
61
|
useEffect(() => {
|
|
59
62
|
apiCall('/stats').then(setStats).catch(() => {});
|
|
60
63
|
apiCall('/agents').then(d => setAgents(d.agents || d || [])).catch(() => {});
|
|
61
|
-
engineCall('/agents?orgId=' +
|
|
64
|
+
engineCall('/agents?orgId=' + effectiveOrgId).then(d => setEngineAgents(d.agents || [])).catch(() => {});
|
|
62
65
|
engineCall('/activity/events?limit=10').then(d => setEvents(d.events || [])).catch(() => {});
|
|
63
|
-
}, []);
|
|
66
|
+
}, [effectiveOrgId]);
|
|
64
67
|
|
|
65
68
|
// Merge admin + engine agents; engine agents (appended last) win in the data map
|
|
66
69
|
var mergedForMap = [].concat(agents, engineAgents);
|
|
@@ -73,6 +76,7 @@ export function DashboardPage() {
|
|
|
73
76
|
var _tip = { marginTop: 12, padding: 12, background: 'var(--bg-secondary, #1e293b)', borderRadius: 'var(--radius, 8px)', fontSize: 13 };
|
|
74
77
|
|
|
75
78
|
return h(Fragment, null,
|
|
79
|
+
h(orgCtx.Switcher),
|
|
76
80
|
h(SetupChecklist, { onNavigate: function(pg) { if (navTo) navTo(pg); } }),
|
|
77
81
|
h('div', { className: 'stat-grid' },
|
|
78
82
|
h('div', { className: 'stat-card' }, h('div', { className: 'stat-label', style: { display: 'flex', alignItems: 'center' } }, 'Total Agents', h(HelpButton, { label: 'Total Agents' },
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { h, useState, useEffect, useCallback, Fragment, useApp, engineCall, buildAgentEmailMap, resolveAgentEmail, buildAgentDataMap, renderAgentBadge, getOrgId } from '../components/utils.js';
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { HelpButton } from '../components/help-button.js';
|
|
4
|
+
import { useOrgContext } from '../components/org-switcher.js';
|
|
4
5
|
|
|
5
6
|
// ─── Constants ──────────────────────────────────────────
|
|
6
7
|
|
|
@@ -93,6 +94,8 @@ function EmptyState(props) {
|
|
|
93
94
|
// ─── Main Page ──────────────────────────────────────────
|
|
94
95
|
|
|
95
96
|
export function GuardrailsPage() {
|
|
97
|
+
var orgCtx = useOrgContext();
|
|
98
|
+
var effectiveOrgId = orgCtx.selectedOrgId || getOrgId();
|
|
96
99
|
var app = useApp();
|
|
97
100
|
var toast = app.toast;
|
|
98
101
|
var tab = useState('overview');
|
|
@@ -101,7 +104,7 @@ export function GuardrailsPage() {
|
|
|
101
104
|
var _ag = useState([]);
|
|
102
105
|
var agents = _ag[0]; var setAgents = _ag[1];
|
|
103
106
|
useEffect(function() {
|
|
104
|
-
engineCall('/agents?orgId=' +
|
|
107
|
+
engineCall('/agents?orgId=' + effectiveOrgId).then(function(d) { setAgents(d.agents || []); }).catch(function() {});
|
|
105
108
|
}, []);
|
|
106
109
|
|
|
107
110
|
var TABS = [
|
|
@@ -164,8 +167,8 @@ function OverviewTab(props) {
|
|
|
164
167
|
var load = function() {
|
|
165
168
|
setLoading(true);
|
|
166
169
|
Promise.all([
|
|
167
|
-
engineCall('/guardrails/interventions?orgId=' +
|
|
168
|
-
engineCall('/policies?orgId=' +
|
|
170
|
+
engineCall('/guardrails/interventions?orgId=' + effectiveOrgId + '&limit=10').catch(function() { return { interventions: [] }; }),
|
|
171
|
+
engineCall('/policies?orgId=' + effectiveOrgId).catch(function() { return { policies: [] }; }),
|
|
169
172
|
engineCall('/onboarding/org/default').catch(function() { return { progress: [] }; }),
|
|
170
173
|
]).then(function(res) {
|
|
171
174
|
setInterventions(res[0].interventions || []);
|
|
@@ -203,6 +206,7 @@ function OverviewTab(props) {
|
|
|
203
206
|
var typeColor = function(t) { return t === 'kill' ? '#ef4444' : t === 'pause' ? '#f59e0b' : t === 'resume' ? '#15803d' : '#0ea5e9'; };
|
|
204
207
|
|
|
205
208
|
return h(Fragment, null,
|
|
209
|
+
h(orgCtx.Switcher),
|
|
206
210
|
// Quick action bar
|
|
207
211
|
h('div', { className: 'card', style: { marginBottom: 16 } },
|
|
208
212
|
h('div', { className: 'card-body', style: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' } },
|
|
@@ -282,17 +286,17 @@ function PoliciesTab() {
|
|
|
282
286
|
var editPolicy = _edit[0]; var setEditPolicy = _edit[1];
|
|
283
287
|
var _exp = useState(null);
|
|
284
288
|
var expanded = _exp[0]; var setExpanded = _exp[1];
|
|
285
|
-
var _form = useState({ orgId:
|
|
289
|
+
var _form = useState({ orgId: effectiveOrgId, name: '', category: 'code_of_conduct', description: '', content: '', priority: 0, enforcement: 'mandatory', appliesTo: ['*'], tags: [], enabled: true });
|
|
286
290
|
var form = _form[0]; var setForm = _form[1];
|
|
287
291
|
|
|
288
292
|
var load = function() {
|
|
289
|
-
engineCall('/policies?orgId=' +
|
|
293
|
+
engineCall('/policies?orgId=' + effectiveOrgId).then(function(d) { setPolicies(d.policies || []); }).catch(function() {});
|
|
290
294
|
};
|
|
291
295
|
useEffect(load, []);
|
|
292
296
|
|
|
293
297
|
var openCreate = function() {
|
|
294
298
|
setEditPolicy(null);
|
|
295
|
-
setForm({ orgId:
|
|
299
|
+
setForm({ orgId: effectiveOrgId, name: '', category: 'code_of_conduct', description: '', content: '', priority: 0, enforcement: 'mandatory', appliesTo: ['*'], tags: [], enabled: true });
|
|
296
300
|
setShowModal(true);
|
|
297
301
|
};
|
|
298
302
|
var openEdit = function(p) {
|
|
@@ -314,7 +318,7 @@ function PoliciesTab() {
|
|
|
314
318
|
.catch(function(e) { toast(e.message, 'error'); });
|
|
315
319
|
};
|
|
316
320
|
var applyDefaults = function() {
|
|
317
|
-
engineCall('/policies/templates/apply', { method: 'POST', body: JSON.stringify({ orgId:
|
|
321
|
+
engineCall('/policies/templates/apply', { method: 'POST', body: JSON.stringify({ orgId: effectiveOrgId, createdBy: 'admin' }) })
|
|
318
322
|
.then(function(d) { toast('Applied ' + (d.policies ? d.policies.length : 0) + ' default templates', 'success'); load(); })
|
|
319
323
|
.catch(function(e) { toast(e.message, 'error'); });
|
|
320
324
|
};
|
|
@@ -451,7 +455,7 @@ function OnboardingTab(props) {
|
|
|
451
455
|
|
|
452
456
|
var initiate = function() {
|
|
453
457
|
if (!initAgentId) { toast('Enter an agent ID', 'error'); return; }
|
|
454
|
-
engineCall('/onboarding/initiate/' + initAgentId, { method: 'POST', body: JSON.stringify({ orgId:
|
|
458
|
+
engineCall('/onboarding/initiate/' + initAgentId, { method: 'POST', body: JSON.stringify({ orgId: effectiveOrgId }) })
|
|
455
459
|
.then(function() { toast('Onboarding initiated', 'success'); setInitAgentId(''); load(); })
|
|
456
460
|
.catch(function(e) { toast(e.message, 'error'); });
|
|
457
461
|
};
|
|
@@ -461,7 +465,7 @@ function OnboardingTab(props) {
|
|
|
461
465
|
.catch(function(e) { toast(e.message, 'error'); });
|
|
462
466
|
};
|
|
463
467
|
var checkChanges = function() {
|
|
464
|
-
engineCall('/onboarding/check-changes', { method: 'POST', body: JSON.stringify({ orgId:
|
|
468
|
+
engineCall('/onboarding/check-changes', { method: 'POST', body: JSON.stringify({ orgId: effectiveOrgId }) })
|
|
465
469
|
.then(function(d) {
|
|
466
470
|
var stale = d.staleAgents || [];
|
|
467
471
|
if (stale.length === 0) { toast('All agents up to date', 'success'); }
|
|
@@ -555,7 +559,7 @@ function MemoryTab(props) {
|
|
|
555
559
|
var showCreate = _show[0]; var setShowCreate = _show[1];
|
|
556
560
|
var _exp = useState(null);
|
|
557
561
|
var expanded = _exp[0]; var setExpanded = _exp[1];
|
|
558
|
-
var _form = useState({ agentId: '', orgId:
|
|
562
|
+
var _form = useState({ agentId: '', orgId: effectiveOrgId, category: 'org_knowledge', title: '', content: '', source: 'admin', importance: 'normal', tags: [] });
|
|
559
563
|
var form = _form[0]; var setForm = _form[1];
|
|
560
564
|
|
|
561
565
|
var loadMemories = function(aid) {
|
|
@@ -762,13 +766,13 @@ function RulesTab(props) {
|
|
|
762
766
|
var _showAnomaly = useState(false);
|
|
763
767
|
var showAnomalyModal = _showAnomaly[0]; var setShowAnomalyModal = _showAnomaly[1];
|
|
764
768
|
var _form = useState({
|
|
765
|
-
orgId:
|
|
769
|
+
orgId: effectiveOrgId, name: '', description: '', category: 'anomaly', ruleType: 'threshold',
|
|
766
770
|
conditions: { threshold: 10, windowMinutes: 60 },
|
|
767
771
|
action: 'alert', severity: 'medium', cooldownMinutes: 15, enabled: true
|
|
768
772
|
});
|
|
769
773
|
var form = _form[0]; var setForm = _form[1];
|
|
770
774
|
var _anomalyForm = useState({
|
|
771
|
-
orgId:
|
|
775
|
+
orgId: effectiveOrgId, name: '', ruleType: 'error_rate',
|
|
772
776
|
config: { maxErrorsPerHour: 50, windowMinutes: 60 }, action: 'pause', enabled: true
|
|
773
777
|
});
|
|
774
778
|
var anomalyForm = _anomalyForm[0]; var setAnomalyForm = _anomalyForm[1];
|
|
@@ -777,9 +781,9 @@ function RulesTab(props) {
|
|
|
777
781
|
|
|
778
782
|
var load = function() {
|
|
779
783
|
Promise.all([
|
|
780
|
-
engineCall('/guardrails/rules?orgId=' +
|
|
781
|
-
engineCall('/anomaly-rules?orgId=' +
|
|
782
|
-
engineCall('/guardrails/interventions?orgId=' +
|
|
784
|
+
engineCall('/guardrails/rules?orgId=' + effectiveOrgId).catch(function() { return { rules: [] }; }),
|
|
785
|
+
engineCall('/anomaly-rules?orgId=' + effectiveOrgId).catch(function() { return { rules: [] }; }),
|
|
786
|
+
engineCall('/guardrails/interventions?orgId=' + effectiveOrgId + '&limit=50').catch(function() { return { interventions: [] }; }),
|
|
783
787
|
]).then(function(res) {
|
|
784
788
|
setRules(res[0].rules || []);
|
|
785
789
|
setAnomalyRules(res[1].rules || []);
|
|
@@ -791,7 +795,7 @@ function RulesTab(props) {
|
|
|
791
795
|
// Guardrail rules CRUD
|
|
792
796
|
var openCreateRule = function() {
|
|
793
797
|
setEditRule(null);
|
|
794
|
-
setForm({ orgId:
|
|
798
|
+
setForm({ orgId: effectiveOrgId, name: '', description: '', category: 'anomaly', ruleType: 'threshold', conditions: { threshold: 10, windowMinutes: 60 }, action: 'alert', severity: 'medium', cooldownMinutes: 15, enabled: true });
|
|
795
799
|
setShowModal(true);
|
|
796
800
|
};
|
|
797
801
|
var openEditRule = function(r) {
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { h, useState, useEffect, Fragment, useApp, engineCall, buildAgentEmailMap, buildAgentDataMap, resolveAgentEmail, renderAgentBadge, getOrgId } from '../components/utils.js';
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { HelpButton } from '../components/help-button.js';
|
|
4
|
+
import { useOrgContext } from '../components/org-switcher.js';
|
|
4
5
|
|
|
5
6
|
export function JournalPage() {
|
|
7
|
+
var orgCtx = useOrgContext();
|
|
8
|
+
var effectiveOrgId = orgCtx.selectedOrgId || getOrgId();
|
|
6
9
|
const { toast } = useApp();
|
|
7
10
|
const [entries, setEntries] = useState([]);
|
|
8
11
|
const [total, setTotal] = useState(0);
|
|
@@ -11,9 +14,9 @@ export function JournalPage() {
|
|
|
11
14
|
const [agents, setAgents] = useState([]);
|
|
12
15
|
|
|
13
16
|
const load = () => {
|
|
14
|
-
engineCall('/journal?orgId=' +
|
|
17
|
+
engineCall('/journal?orgId=' + effectiveOrgId + '&limit=50').then(d => { setEntries(d.entries || []); setTotal(d.total || 0); }).catch(() => {});
|
|
15
18
|
engineCall('/journal/stats/default').then(d => setStats(d)).catch(() => {});
|
|
16
|
-
engineCall('/agents?orgId=' +
|
|
19
|
+
engineCall('/agents?orgId=' + effectiveOrgId).then(d => setAgents(d.agents || [])).catch(() => {});
|
|
17
20
|
};
|
|
18
21
|
useEffect(load, []);
|
|
19
22
|
|
|
@@ -29,6 +32,7 @@ export function JournalPage() {
|
|
|
29
32
|
var _tip = { marginTop: 12, padding: 12, background: 'var(--bg-secondary, #1e293b)', borderRadius: 'var(--radius, 8px)', fontSize: 13 };
|
|
30
33
|
|
|
31
34
|
return h('div', { className: 'page-inner' },
|
|
35
|
+
h(orgCtx.Switcher),
|
|
32
36
|
h('div', { className: 'page-header' }, h('h1', { style: { display: 'flex', alignItems: 'center' } }, 'Action Journal', h(HelpButton, { label: 'Action Journal' },
|
|
33
37
|
h('p', null, 'A tamper-proof log of every action agents have taken. Think of it as an audit trail — every tool call, every side effect, recorded with full context.'),
|
|
34
38
|
h('h4', { style: _h4 }, 'Why it matters'),
|
|
@@ -2,9 +2,11 @@ import { h, useState, useEffect, useCallback, Fragment, useApp, engineCall, buil
|
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { Modal } from '../components/modal.js';
|
|
4
4
|
import { HelpButton } from '../components/help-button.js';
|
|
5
|
+
import { useOrgContext } from '../components/org-switcher.js';
|
|
5
6
|
|
|
6
7
|
export function KnowledgeContributionsPage() {
|
|
7
8
|
var { toast } = useApp();
|
|
9
|
+
var orgCtx = useOrgContext();
|
|
8
10
|
var [tab, setTab] = useState('bases');
|
|
9
11
|
var [bases, setBases] = useState([]);
|
|
10
12
|
var [roles, setRoles] = useState([]);
|
|
@@ -39,14 +41,17 @@ export function KnowledgeContributionsPage() {
|
|
|
39
41
|
var [searchDays, setSearchDays] = useState(7);
|
|
40
42
|
var [searchAgentFilter, setSearchAgentFilter] = useState('');
|
|
41
43
|
|
|
44
|
+
// Effective org ID: uses client org if selected, else default
|
|
45
|
+
var effectiveOrgId = orgCtx.selectedOrgId || effectiveOrgId;
|
|
46
|
+
|
|
42
47
|
var loadBases = useCallback(function() {
|
|
43
48
|
Promise.all([
|
|
44
|
-
engineCall('/knowledge-contribution/bases?orgId=' +
|
|
49
|
+
engineCall('/knowledge-contribution/bases?orgId=' + effectiveOrgId).catch(function() { return { bases: [] }; }),
|
|
45
50
|
engineCall('/knowledge-bases').catch(function() { return { knowledgeBases: [] }; })
|
|
46
51
|
]).then(function(results) {
|
|
47
52
|
var contribBases = results[0].bases || [];
|
|
48
53
|
var mainBases = (results[1].knowledgeBases || []).map(function(kb) {
|
|
49
|
-
return { id: kb.id, orgId:
|
|
54
|
+
return { id: kb.id, orgId: effectiveOrgId, name: kb.name, description: kb.description, role: 'general', categories: [], contributorCount: 0, entryCount: kb.stats ? kb.stats.documentCount || 0 : 0, createdAt: kb.createdAt, updatedAt: kb.updatedAt, _source: 'main' };
|
|
50
55
|
});
|
|
51
56
|
// Merge: contribution bases first, then main bases not already in contribution
|
|
52
57
|
var ids = {};
|
|
@@ -63,19 +68,19 @@ export function KnowledgeContributionsPage() {
|
|
|
63
68
|
}, []);
|
|
64
69
|
|
|
65
70
|
var loadStats = useCallback(function() {
|
|
66
|
-
engineCall('/knowledge-contribution/stats?orgId=' +
|
|
71
|
+
engineCall('/knowledge-contribution/stats?orgId=' + effectiveOrgId)
|
|
67
72
|
.then(function(d) { setStats(d || {}); })
|
|
68
73
|
.catch(function() {});
|
|
69
74
|
}, []);
|
|
70
75
|
|
|
71
76
|
var loadContributions = useCallback(function() {
|
|
72
|
-
engineCall('/knowledge-contribution/contributions?orgId=' +
|
|
77
|
+
engineCall('/knowledge-contribution/contributions?orgId=' + effectiveOrgId)
|
|
73
78
|
.then(function(d) { setContributions(d.contributions || d.cycles || []); })
|
|
74
79
|
.catch(function() {});
|
|
75
80
|
}, []);
|
|
76
81
|
|
|
77
82
|
var loadSchedules = useCallback(function() {
|
|
78
|
-
engineCall('/knowledge-contribution/schedules?orgId=' +
|
|
83
|
+
engineCall('/knowledge-contribution/schedules?orgId=' + effectiveOrgId)
|
|
79
84
|
.then(function(d) { setSchedules(d.schedules || []); })
|
|
80
85
|
.catch(function() {});
|
|
81
86
|
}, []);
|
|
@@ -86,10 +91,10 @@ export function KnowledgeContributionsPage() {
|
|
|
86
91
|
loadStats();
|
|
87
92
|
loadContributions();
|
|
88
93
|
loadSchedules();
|
|
89
|
-
engineCall('/agents?orgId=' +
|
|
94
|
+
engineCall('/agents?orgId=' + effectiveOrgId).then(function(d) { setAgents(d.agents || []); }).catch(function() {});
|
|
90
95
|
}, [loadBases, loadRoles, loadStats, loadContributions, loadSchedules]);
|
|
91
96
|
|
|
92
|
-
useEffect(function() { load(); }, [load]);
|
|
97
|
+
useEffect(function() { load(); }, [load, effectiveOrgId]);
|
|
93
98
|
|
|
94
99
|
var loadBaseEntries = useCallback(function(baseId) {
|
|
95
100
|
var params = new URLSearchParams();
|
|
@@ -110,7 +115,7 @@ export function KnowledgeContributionsPage() {
|
|
|
110
115
|
try {
|
|
111
116
|
await engineCall('/knowledge-contribution/bases', {
|
|
112
117
|
method: 'POST',
|
|
113
|
-
body: JSON.stringify({ name: baseForm.name, description: baseForm.description, role: baseForm.role, orgId:
|
|
118
|
+
body: JSON.stringify({ name: baseForm.name, description: baseForm.description, role: baseForm.role, orgId: effectiveOrgId })
|
|
114
119
|
});
|
|
115
120
|
toast('Knowledge base created', 'success');
|
|
116
121
|
setShowCreateBase(false);
|
|
@@ -161,7 +166,7 @@ export function KnowledgeContributionsPage() {
|
|
|
161
166
|
try {
|
|
162
167
|
await engineCall('/knowledge-contribution/contribute/' + triggerAgent, {
|
|
163
168
|
method: 'POST',
|
|
164
|
-
body: JSON.stringify({ targetBaseId: triggerBase || undefined, orgId:
|
|
169
|
+
body: JSON.stringify({ targetBaseId: triggerBase || undefined, orgId: effectiveOrgId })
|
|
165
170
|
});
|
|
166
171
|
toast('Contribution triggered', 'success');
|
|
167
172
|
setShowTrigger(false);
|
|
@@ -182,7 +187,7 @@ export function KnowledgeContributionsPage() {
|
|
|
182
187
|
frequency: scheduleForm.frequency,
|
|
183
188
|
dayOfWeek: scheduleForm.dayOfWeek,
|
|
184
189
|
minConfidence: parseFloat(scheduleForm.minConfidence) || 0.7,
|
|
185
|
-
orgId:
|
|
190
|
+
orgId: effectiveOrgId
|
|
186
191
|
})
|
|
187
192
|
});
|
|
188
193
|
toast('Schedule created', 'success');
|
|
@@ -1375,6 +1380,9 @@ export function KnowledgeContributionsPage() {
|
|
|
1375
1380
|
};
|
|
1376
1381
|
|
|
1377
1382
|
return h(Fragment, null,
|
|
1383
|
+
// Org context switcher
|
|
1384
|
+
h(orgCtx.Switcher),
|
|
1385
|
+
|
|
1378
1386
|
// Header
|
|
1379
1387
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
|
|
1380
1388
|
h('div', null,
|