@taskforcehq/taskforce 0.3.319 → 0.3.320

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.
@@ -579,14 +579,44 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
579
579
  persistWorkspaceSyncStatePatch,
580
580
  refreshSetupContext
581
581
  ]);
582
- const nextPath = useMemo(() => {
582
+ const nextTarget = useMemo(() => {
583
583
  const raw = new URLSearchParams(location.search).get('next') || '/';
584
- if (!raw.startsWith('/'))
585
- return '/';
586
- if (raw === '/login')
584
+ if (raw.startsWith('/')) {
585
+ if (raw === '/login')
586
+ return '/';
587
+ return raw;
588
+ }
589
+ try {
590
+ const parsed = new URL(raw);
591
+ const host = parsed.hostname.toLowerCase();
592
+ const isLocal = host === 'localhost' || host === '127.0.0.1' || host === '::1';
593
+ const isTaskforceHost = host === 'taskforcehq.ai' || host.endsWith('.taskforcehq.ai');
594
+ if ((parsed.protocol === 'http:' || parsed.protocol === 'https:') && (isLocal || isTaskforceHost)) {
595
+ return parsed.toString();
596
+ }
597
+ }
598
+ catch {
587
599
  return '/';
588
- return raw;
600
+ }
601
+ return '/';
589
602
  }, [location.search]);
603
+ const nextPath = useMemo(() => nextTarget.startsWith('/') ? nextTarget : '/', [nextTarget]);
604
+ const navigateToPostAuthTarget = useCallback((target, options) => {
605
+ if (!target)
606
+ return;
607
+ if (target.startsWith('/')) {
608
+ navigate(target, { replace: options?.replace === true });
609
+ return;
610
+ }
611
+ if (typeof window !== 'undefined') {
612
+ if (options?.replace === true) {
613
+ window.location.replace(target);
614
+ }
615
+ else {
616
+ window.location.assign(target);
617
+ }
618
+ }
619
+ }, [navigate]);
590
620
  const shouldReturnToCloudWorkspaceSelection = useMemo(() => {
591
621
  try {
592
622
  const parsed = new URL(nextPath, 'https://taskforce.local');
@@ -622,9 +652,9 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
622
652
  interval: selectedSignupPlanVersionId ? resolvedSignupInterval : null
623
653
  });
624
654
  }
625
- return nextPath;
655
+ return nextTarget;
626
656
  }, [
627
- nextPath,
657
+ nextTarget,
628
658
  planSelectionPath,
629
659
  resolvedSignupInterval,
630
660
  selectedSignupPlanId,
@@ -688,9 +718,13 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
688
718
  ? resolveWorkspaceSetupPath()
689
719
  : pendingPostAuthRedirect.path;
690
720
  const currentRoute = `${location.pathname}${location.search}${location.hash || ''}`;
691
- if (currentRoute !== destination) {
692
- navigate(destination, { replace: true });
721
+ if (destination.startsWith('/')) {
722
+ if (currentRoute !== destination) {
723
+ navigate(destination, { replace: true });
724
+ }
725
+ return;
693
726
  }
727
+ navigateToPostAuthTarget(destination, { replace: true });
694
728
  }, [
695
729
  authSessionResolved,
696
730
  cloudAccountAccessPending,
@@ -699,6 +733,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
699
733
  location.pathname,
700
734
  location.search,
701
735
  navigate,
736
+ navigateToPostAuthTarget,
702
737
  pendingPostAuthRedirect,
703
738
  resolveWorkspaceSetupPath
704
739
  ]);
@@ -726,9 +761,13 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
726
761
  ? pendingPostAuthRedirect.path
727
762
  : planSelectionPath;
728
763
  const currentRoute = `${location.pathname}${location.search}${location.hash || ''}`;
729
- if (currentRoute !== destination) {
730
- navigate(destination, { replace: true });
764
+ if (destination.startsWith('/')) {
765
+ if (currentRoute !== destination) {
766
+ navigate(destination, { replace: true });
767
+ }
768
+ return;
731
769
  }
770
+ navigateToPostAuthTarget(destination, { replace: true });
732
771
  }, [
733
772
  accountAccessGate?.canAccessApp,
734
773
  accountAccessGateResolved,
@@ -739,6 +778,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
739
778
  location.pathname,
740
779
  location.search,
741
780
  navigate,
781
+ navigateToPostAuthTarget,
742
782
  pendingPostAuthRedirect,
743
783
  planSelectionPath
744
784
  ]);
@@ -901,8 +941,9 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
901
941
  if (shouldDelayAuthenticatedLoginRedirect) {
902
942
  return;
903
943
  }
904
- if (isLoginRoute && isAuthenticated && !shouldStayOnInviteRoute)
905
- navigate(nextPath, { replace: true });
944
+ if (isLoginRoute && isAuthenticated && !shouldStayOnInviteRoute) {
945
+ navigateToPostAuthTarget(nextTarget, { replace: true });
946
+ }
906
947
  }
907
948
  else {
908
949
  if (!isLoginRoute) {
@@ -1299,7 +1340,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
1299
1340
  setLoginBusy(false);
1300
1341
  return;
1301
1342
  }
1302
- navigate(nextPath, { replace: true });
1343
+ navigateToPostAuthTarget(nextTarget, { replace: true });
1303
1344
  }
1304
1345
  else if (authMode === 'login') {
1305
1346
  if (inviteToken.trim()) {
@@ -1319,14 +1360,14 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
1319
1360
  setLoginDisplayName('');
1320
1361
  setLoginPassword('');
1321
1362
  setLoginPasswordConfirm('');
1322
- navigate(nextPath, { replace: true });
1363
+ navigateToPostAuthTarget(nextTarget, { replace: true });
1323
1364
  }
1324
1365
  else {
1325
1366
  setLoginEmail('');
1326
1367
  setLoginDisplayName('');
1327
1368
  setLoginPassword('');
1328
1369
  setLoginPasswordConfirm('');
1329
- navigate(nextPath, { replace: true });
1370
+ navigateToPostAuthTarget(nextTarget, { replace: true });
1330
1371
  }
1331
1372
  setLoginBusy(false);
1332
1373
  }, children: loginBusy ? 'Working...' : (authMode === 'login' ? 'Sign In'
@@ -262,7 +262,7 @@ export function registerAdminRoutes(deps) {
262
262
  const scopeLabels = {
263
263
  'tasks:read': 'Read tasks',
264
264
  'tasks:write': 'Update tasks',
265
- 'workspace:read': 'Read workspace details'
265
+ 'workspace:read': 'Read workspace'
266
266
  };
267
267
  const options = input.workspaces.map((workspace) => {
268
268
  const selected = workspace.id === input.preselectedWorkspaceId ? ' selected' : '';
@@ -285,25 +285,19 @@ export function registerAdminRoutes(deps) {
285
285
  :root { color-scheme: dark; }
286
286
  body { margin: 0; font-family: "Outfit", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: radial-gradient(circle at top, rgba(255,138,0,0.08), transparent 30%), #111126; color: #f8f7ff; }
287
287
  .wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 32px 24px; }
288
- .card { width: min(680px, 100%); background: rgba(22,22,44,0.95); border: 1px solid rgba(255,255,255,0.08); border-radius: 36px; box-shadow: 0 24px 80px rgba(0,0,0,0.35); padding: 44px 56px; }
289
- .eyebrow { display: inline-flex; align-items: center; gap: 10px; margin-bottom: 18px; color: rgba(255,255,255,0.55); font-size: 12px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; }
290
- .eyebrow-mark { width: 10px; height: 10px; border-radius: 999px; background: linear-gradient(135deg, #ff8a00 0%, #8d6bff 100%); box-shadow: 0 0 18px rgba(255,138,0,0.35); }
291
- h1 { margin: 0; font-size: clamp(2.25rem, 5vw, 3rem); line-height: 0.96; letter-spacing: -0.04em; color: #f8f7ff; }
292
- .brand-word { font-family: "Russo One", sans-serif; font-size: 0.94em; letter-spacing: -0.03em; }
293
- .lede { margin: 18px 0 8px; font-size: 1.15rem; line-height: 1.5; color: rgba(255,255,255,0.78); max-width: 34ch; }
288
+ .card { width: min(720px, 100%); background: rgba(22,22,44,0.95); border: 1px solid rgba(255,255,255,0.08); border-radius: 32px; box-shadow: 0 24px 80px rgba(0,0,0,0.35); padding: 40px 48px; }
289
+ h1 { margin: 0; font-size: clamp(1.95rem, 4.2vw, 2.6rem); line-height: 1.02; letter-spacing: -0.035em; color: #f8f7ff; }
290
+ .lede { margin: 16px 0 8px; font-size: 1.1rem; line-height: 1.45; color: rgba(255,255,255,0.78); max-width: 48ch; text-wrap: pretty; }
294
291
  .lede strong { color: #fff; }
295
- .user-meta { margin: 0 0 28px; color: rgba(255,255,255,0.5); font-size: 0.9rem; }
292
+ .session-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 0 0 28px; }
293
+ .user-meta { margin: 0; color: rgba(255,255,255,0.5); font-size: 0.9rem; }
294
+ .account-switch { padding: 0; border: 0; min-width: 0; background: transparent; color: #cbd8ff; font-size: 0.9rem; font-weight: 600; box-shadow: none; }
295
+ .account-switch:hover { transform: none; color: #ffffff; text-decoration: underline; }
296
296
  label { display: block; font-size: 13px; font-weight: 700; margin: 24px 0 10px; color: rgba(255,255,255,0.92); letter-spacing: 0.01em; }
297
297
  select { width: 100%; border-radius: 18px; border: 1px solid rgba(255,255,255,0.1); background: #1f2039; color: #fff; padding: 16px 18px; font-size: 15px; font-weight: 600; box-shadow: inset 0 1px 0 rgba(255,255,255,0.02); }
298
298
  .scopes { display:flex; flex-wrap:wrap; gap:10px; margin-top:8px; }
299
299
  .scope-chip { display:inline-flex; padding:8px 14px; border-radius:999px; background:#2a2947; border:1px solid rgba(255,255,255,0.09); font-size:13px; font-weight:600; color:rgba(255,255,255,0.92); }
300
- .details { margin-top: 24px; padding: 0; border: 1px solid rgba(255,255,255,0.08); border-radius: 18px; background: rgba(255,255,255,0.04); overflow: hidden; }
301
- .details summary { list-style: none; cursor: pointer; padding: 16px 18px; font-size: 0.92rem; font-weight: 700; color: rgba(255,255,255,0.86); }
302
- .details summary::-webkit-details-marker { display: none; }
303
- .details-body { padding: 0 18px 18px; color: rgba(255,255,255,0.68); font-size: 0.9rem; line-height: 1.5; }
304
- .details-row { margin: 0 0 8px; }
305
- .details-row strong { color: rgba(255,255,255,0.92); }
306
- .legal { margin: 24px 2px 0; color: rgba(255,255,255,0.58); font-size: 0.9rem; line-height: 1.55; max-width: 56ch; }
300
+ .legal { margin: 28px 2px 0; color: rgba(255,255,255,0.58); font-size: 0.9rem; line-height: 1.55; max-width: 58ch; }
307
301
  .error { margin-bottom: 18px; padding: 12px 14px; border-radius: 14px; background: rgba(255,99,132,0.12); color: #ffb6c6; border: 1px solid rgba(255,99,132,0.25); }
308
302
  .actions { display:flex; justify-content:flex-end; gap:14px; margin-top: 36px; }
309
303
  button { border-radius: 18px; padding: 15px 22px; font-size: 15px; font-weight: 700; cursor: pointer; border: 1px solid rgba(255,255,255,0.08); min-width: 156px; transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease, background-color 160ms ease; }
@@ -313,7 +307,8 @@ export function registerAdminRoutes(deps) {
313
307
  a { color: #cbd8ff; }
314
308
  @media (max-width: 720px) {
315
309
  .card { padding: 30px 22px; border-radius: 28px; }
316
- h1 { font-size: 2rem; }
310
+ h1 { font-size: 1.85rem; }
311
+ .lede { max-width: none; }
317
312
  .actions { flex-direction: column-reverse; }
318
313
  button { width: 100%; }
319
314
  }
@@ -322,22 +317,17 @@ export function registerAdminRoutes(deps) {
322
317
  <body>
323
318
  <div class="wrap">
324
319
  <form class="card" method="post" action="/oauth/mcp/authorize">
325
- <div class="eyebrow"><span class="eyebrow-mark"></span>Taskforce MCP</div>
326
- <h1>Authorize <span class="brand-word">Taskforce</span> Connector</h1>
320
+ <h1>Authorize Taskforce Connector</h1>
327
321
  <p class="lede"><strong>${escapeHtml(input.clientName)}</strong> wants to connect to Taskforce for <strong>${escapeHtml(input.userName)}</strong>.</p>
328
- ${input.userMeta ? `<p class="user-meta">Signed in as ${escapeHtml(input.userMeta)}</p>` : ''}
322
+ <div class="session-row">
323
+ ${input.userMeta ? `<p class="user-meta">Signed in as ${escapeHtml(input.userMeta)}</p>` : ''}
324
+ <button class="account-switch" type="submit" formaction="/api/taskforce/auth/logout" formmethod="post" formnovalidate>Use different account</button>
325
+ </div>
329
326
  ${input.error ? `<div class="error">${escapeHtml(input.error)}</div>` : ''}
330
- <label for="workspaceId">Workspace</label>
327
+ <label for="workspaceId">Select Workspace</label>
331
328
  <select id="workspaceId" name="workspaceId" required>${options}</select>
332
329
  <label>Requested Permissions</label>
333
330
  <div class="scopes">${scopeBadges}</div>
334
- <details class="details">
335
- <summary>Technical details</summary>
336
- <div class="details-body">
337
- <div class="details-row"><strong>Client:</strong> ${escapeHtml(input.clientName)}</div>
338
- <div class="details-row"><strong>Redirect URI:</strong> ${escapeHtml(input.redirectUri)}</div>
339
- </div>
340
- </details>
341
331
  <p class="legal">By authorizing this connector, you agree to Taskforce&apos;s <a href="${termsUrl}" target="_blank" rel="noopener noreferrer">Terms of Service</a> and acknowledge the <a href="${privacyUrl}" target="_blank" rel="noopener noreferrer">Privacy Policy</a>.</p>
342
332
  <input type="hidden" name="client_id" value="${escapeHtml(input.clientId)}" />
343
333
  <input type="hidden" name="redirect_uri" value="${escapeHtml(input.redirectUri)}" />
@@ -345,6 +335,7 @@ export function registerAdminRoutes(deps) {
345
335
  <input type="hidden" name="state" value="${escapeHtml(input.state)}" />
346
336
  <input type="hidden" name="code_challenge" value="${escapeHtml(input.codeChallenge)}" />
347
337
  <input type="hidden" name="code_challenge_method" value="${escapeHtml(input.codeChallengeMethod)}" />
338
+ <input type="hidden" name="next" value="${escapeHtml(input.authorizeUrl)}" />
348
339
  <div class="actions">
349
340
  <button type="submit" name="decision" value="deny">Cancel</button>
350
341
  <button type="submit" name="decision" value="approve">Authorize</button>
@@ -2667,8 +2658,18 @@ export function registerAdminRoutes(deps) {
2667
2658
  const userIdentity = core.resolveUserIdentity(access.userId);
2668
2659
  const userName = String(userIdentity?.displayName || userIdentity?.email || access.userId).trim() || String(access.userId);
2669
2660
  const userMeta = userIdentity?.email && userIdentity.email !== userName ? String(userIdentity.email).trim() : null;
2661
+ const authorizeUrl = new URL('/oauth/mcp/authorize', resolveMcpOauthIssuer(req));
2662
+ authorizeUrl.searchParams.set('client_id', clientId);
2663
+ authorizeUrl.searchParams.set('redirect_uri', redirectUri);
2664
+ authorizeUrl.searchParams.set('response_type', responseType);
2665
+ authorizeUrl.searchParams.set('code_challenge', codeChallenge);
2666
+ authorizeUrl.searchParams.set('code_challenge_method', codeChallengeMethod);
2667
+ if (state)
2668
+ authorizeUrl.searchParams.set('state', state);
2669
+ if (scopes.length > 0)
2670
+ authorizeUrl.searchParams.set('scope', scopes.join(' '));
2670
2671
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
2671
- res.end(renderMcpOauthAuthorizePage({ userName, userMeta, workspaces, preselectedWorkspaceId: preferredWorkspaceId, clientName: clientRecord.clientName, clientId: clientRecord.id, redirectUri, scopes, state, codeChallenge, codeChallengeMethod }));
2672
+ res.end(renderMcpOauthAuthorizePage({ userName, userMeta, authorizeUrl: authorizeUrl.toString(), workspaces, preselectedWorkspaceId: preferredWorkspaceId, clientName: clientRecord.clientName, clientId: clientRecord.id, redirectUri, scopes, state, codeChallenge, codeChallengeMethod }));
2672
2673
  }
2673
2674
  catch (error) {
2674
2675
  const statusCode = resolveErrorStatusCode(error);
@@ -2713,8 +2714,18 @@ export function registerAdminRoutes(deps) {
2713
2714
  const userIdentity = core.resolveUserIdentity(access.userId);
2714
2715
  const userName = String(userIdentity?.displayName || userIdentity?.email || access.userId).trim() || String(access.userId);
2715
2716
  const userMeta = userIdentity?.email && userIdentity.email !== userName ? String(userIdentity.email).trim() : null;
2717
+ const authorizeUrl = new URL('/oauth/mcp/authorize', resolveMcpOauthIssuer(req));
2718
+ authorizeUrl.searchParams.set('client_id', clientId);
2719
+ authorizeUrl.searchParams.set('redirect_uri', redirectUri);
2720
+ authorizeUrl.searchParams.set('response_type', responseType);
2721
+ authorizeUrl.searchParams.set('code_challenge', codeChallenge);
2722
+ authorizeUrl.searchParams.set('code_challenge_method', codeChallengeMethod);
2723
+ if (state)
2724
+ authorizeUrl.searchParams.set('state', state);
2725
+ if (scopes.length > 0)
2726
+ authorizeUrl.searchParams.set('scope', scopes.join(' '));
2716
2727
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
2717
- res.end(renderMcpOauthAuthorizePage({ userName, userMeta, workspaces, preselectedWorkspaceId: preferredWorkspaceId, clientName: clientRecord.clientName, clientId: clientRecord.id, redirectUri, scopes, state, codeChallenge, codeChallengeMethod }));
2728
+ res.end(renderMcpOauthAuthorizePage({ userName, userMeta, authorizeUrl: authorizeUrl.toString(), workspaces, preselectedWorkspaceId: preferredWorkspaceId, clientName: clientRecord.clientName, clientId: clientRecord.id, redirectUri, scopes, state, codeChallenge, codeChallengeMethod }));
2718
2729
  }
2719
2730
  catch (error) {
2720
2731
  const statusCode = resolveErrorStatusCode(error);
@@ -2068,7 +2068,31 @@ export function registerAuthRoutes(deps) {
2068
2068
  for (const domain of domains) {
2069
2069
  clearCookies.push(`${clearBase}; Domain=${domain}`);
2070
2070
  }
2071
+ const body = await parseFormBody(req);
2072
+ const requestedNext = String(body.next || '').trim();
2073
+ const next = (() => {
2074
+ if (!requestedNext)
2075
+ return '';
2076
+ try {
2077
+ const parsed = new URL(requestedNext);
2078
+ const host = parsed.hostname.toLowerCase();
2079
+ const isLocal = host === 'localhost' || host === '127.0.0.1' || host === '::1';
2080
+ const isTaskforceHost = host === 'taskforcehq.ai' || host.endsWith('.taskforcehq.ai');
2081
+ if ((parsed.protocol === 'http:' || parsed.protocol === 'https:') && (isLocal || isTaskforceHost)) {
2082
+ return parsed.toString();
2083
+ }
2084
+ }
2085
+ catch {
2086
+ return '';
2087
+ }
2088
+ return '';
2089
+ })();
2071
2090
  res.setHeader('Set-Cookie', [...new Set(clearCookies)]);
2091
+ if (next) {
2092
+ res.writeHead(303, { Location: next });
2093
+ res.end();
2094
+ return;
2095
+ }
2072
2096
  res.writeHead(200, { 'Content-Type': 'application/json' });
2073
2097
  res.end(JSON.stringify({ success: true }));
2074
2098
  });
@@ -1265,7 +1265,7 @@ export function createRoutes(core, context = {}) {
1265
1265
  const scopeLabels = {
1266
1266
  'tasks:read': 'Read tasks',
1267
1267
  'tasks:write': 'Update tasks',
1268
- 'workspace:read': 'Read workspace details'
1268
+ 'workspace:read': 'Read workspace'
1269
1269
  };
1270
1270
  const options = input.workspaces.map((workspace) => {
1271
1271
  const selected = workspace.id === input.preselectedWorkspaceId ? ' selected' : '';
@@ -1288,25 +1288,19 @@ export function createRoutes(core, context = {}) {
1288
1288
  :root { color-scheme: dark; }
1289
1289
  body { margin: 0; font-family: "Outfit", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: radial-gradient(circle at top, rgba(255,138,0,0.08), transparent 30%), #111126; color: #f8f7ff; }
1290
1290
  .wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 32px 24px; }
1291
- .card { width: min(680px, 100%); background: rgba(22,22,44,0.95); border: 1px solid rgba(255,255,255,0.08); border-radius: 36px; box-shadow: 0 24px 80px rgba(0,0,0,0.35); padding: 44px 56px; }
1292
- .eyebrow { display: inline-flex; align-items: center; gap: 10px; margin-bottom: 18px; color: rgba(255,255,255,0.55); font-size: 12px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; }
1293
- .eyebrow-mark { width: 10px; height: 10px; border-radius: 999px; background: linear-gradient(135deg, #ff8a00 0%, #8d6bff 100%); box-shadow: 0 0 18px rgba(255,138,0,0.35); }
1294
- h1 { margin: 0; font-size: clamp(2.25rem, 5vw, 3rem); line-height: 0.96; letter-spacing: -0.04em; color: #f8f7ff; }
1295
- .brand-word { font-family: "Russo One", sans-serif; font-size: 0.94em; letter-spacing: -0.03em; }
1296
- .lede { margin: 18px 0 8px; font-size: 1.15rem; line-height: 1.5; color: rgba(255,255,255,0.78); max-width: 34ch; }
1291
+ .card { width: min(720px, 100%); background: rgba(22,22,44,0.95); border: 1px solid rgba(255,255,255,0.08); border-radius: 32px; box-shadow: 0 24px 80px rgba(0,0,0,0.35); padding: 40px 48px; }
1292
+ h1 { margin: 0; font-size: clamp(1.95rem, 4.2vw, 2.6rem); line-height: 1.02; letter-spacing: -0.035em; color: #f8f7ff; }
1293
+ .lede { margin: 16px 0 8px; font-size: 1.1rem; line-height: 1.45; color: rgba(255,255,255,0.78); max-width: 48ch; text-wrap: pretty; }
1297
1294
  .lede strong { color: #fff; }
1298
- .user-meta { margin: 0 0 28px; color: rgba(255,255,255,0.5); font-size: 0.9rem; }
1295
+ .session-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 0 0 28px; }
1296
+ .user-meta { margin: 0; color: rgba(255,255,255,0.5); font-size: 0.9rem; }
1297
+ .account-switch { padding: 0; border: 0; min-width: 0; background: transparent; color: #cbd8ff; font-size: 0.9rem; font-weight: 600; box-shadow: none; }
1298
+ .account-switch:hover { transform: none; color: #ffffff; text-decoration: underline; }
1299
1299
  label { display: block; font-size: 13px; font-weight: 700; margin: 24px 0 10px; color: rgba(255,255,255,0.92); letter-spacing: 0.01em; }
1300
1300
  select { width: 100%; border-radius: 18px; border: 1px solid rgba(255,255,255,0.1); background: #1f2039; color: #fff; padding: 16px 18px; font-size: 15px; font-weight: 600; box-shadow: inset 0 1px 0 rgba(255,255,255,0.02); }
1301
1301
  .scopes { display:flex; flex-wrap:wrap; gap:10px; margin-top:8px; }
1302
1302
  .scope-chip { display:inline-flex; padding:8px 14px; border-radius:999px; background:#2a2947; border:1px solid rgba(255,255,255,0.09); font-size:13px; font-weight:600; color:rgba(255,255,255,0.92); }
1303
- .details { margin-top: 24px; padding: 0; border: 1px solid rgba(255,255,255,0.08); border-radius: 18px; background: rgba(255,255,255,0.04); overflow: hidden; }
1304
- .details summary { list-style: none; cursor: pointer; padding: 16px 18px; font-size: 0.92rem; font-weight: 700; color: rgba(255,255,255,0.86); }
1305
- .details summary::-webkit-details-marker { display: none; }
1306
- .details-body { padding: 0 18px 18px; color: rgba(255,255,255,0.68); font-size: 0.9rem; line-height: 1.5; }
1307
- .details-row { margin: 0 0 8px; }
1308
- .details-row strong { color: rgba(255,255,255,0.92); }
1309
- .legal { margin: 24px 2px 0; color: rgba(255,255,255,0.58); font-size: 0.9rem; line-height: 1.55; max-width: 56ch; }
1303
+ .legal { margin: 28px 2px 0; color: rgba(255,255,255,0.58); font-size: 0.9rem; line-height: 1.55; max-width: 58ch; }
1310
1304
  .error { margin-bottom: 18px; padding: 12px 14px; border-radius: 14px; background: rgba(255,99,132,0.12); color: #ffb6c6; border: 1px solid rgba(255,99,132,0.25); }
1311
1305
  .actions { display:flex; justify-content:flex-end; gap:14px; margin-top: 36px; }
1312
1306
  button { border-radius: 18px; padding: 15px 22px; font-size: 15px; font-weight: 700; cursor: pointer; border: 1px solid rgba(255,255,255,0.08); min-width: 156px; transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease, background-color 160ms ease; }
@@ -1316,7 +1310,8 @@ export function createRoutes(core, context = {}) {
1316
1310
  a { color: #cbd8ff; }
1317
1311
  @media (max-width: 720px) {
1318
1312
  .card { padding: 30px 22px; border-radius: 28px; }
1319
- h1 { font-size: 2rem; }
1313
+ h1 { font-size: 1.85rem; }
1314
+ .lede { max-width: none; }
1320
1315
  .actions { flex-direction: column-reverse; }
1321
1316
  button { width: 100%; }
1322
1317
  }
@@ -1325,22 +1320,17 @@ export function createRoutes(core, context = {}) {
1325
1320
  <body>
1326
1321
  <div class="wrap">
1327
1322
  <form class="card" method="post" action="/oauth/mcp/authorize">
1328
- <div class="eyebrow"><span class="eyebrow-mark"></span>Taskforce MCP</div>
1329
- <h1>Authorize <span class="brand-word">Taskforce</span> Connector</h1>
1323
+ <h1>Authorize Taskforce Connector</h1>
1330
1324
  <p class="lede"><strong>${escapeHtml(input.clientName)}</strong> wants to connect to Taskforce for <strong>${escapeHtml(input.userName)}</strong>.</p>
1331
- ${input.userMeta ? `<p class="user-meta">Signed in as ${escapeHtml(input.userMeta)}</p>` : ''}
1325
+ <div class="session-row">
1326
+ ${input.userMeta ? `<p class="user-meta">Signed in as ${escapeHtml(input.userMeta)}</p>` : ''}
1327
+ <button class="account-switch" type="submit" formaction="/api/taskforce/auth/logout" formmethod="post" formnovalidate>Use different account</button>
1328
+ </div>
1332
1329
  ${input.error ? `<div class="error">${escapeHtml(input.error)}</div>` : ''}
1333
- <label for="workspaceId">Workspace</label>
1330
+ <label for="workspaceId">Select Workspace</label>
1334
1331
  <select id="workspaceId" name="workspaceId" required>${options}</select>
1335
1332
  <label>Requested Permissions</label>
1336
1333
  <div class="scopes">${scopeBadges}</div>
1337
- <details class="details">
1338
- <summary>Technical details</summary>
1339
- <div class="details-body">
1340
- <div class="details-row"><strong>Client:</strong> ${escapeHtml(input.clientName)}</div>
1341
- <div class="details-row"><strong>Redirect URI:</strong> ${escapeHtml(input.redirectUri)}</div>
1342
- </div>
1343
- </details>
1344
1334
  <p class="legal">By authorizing this connector, you agree to Taskforce&apos;s <a href="${termsUrl}" target="_blank" rel="noopener noreferrer">Terms of Service</a> and acknowledge the <a href="${privacyUrl}" target="_blank" rel="noopener noreferrer">Privacy Policy</a>.</p>
1345
1335
  <input type="hidden" name="client_id" value="${escapeHtml(input.clientId)}" />
1346
1336
  <input type="hidden" name="redirect_uri" value="${escapeHtml(input.redirectUri)}" />
@@ -1348,6 +1338,7 @@ export function createRoutes(core, context = {}) {
1348
1338
  <input type="hidden" name="state" value="${escapeHtml(input.state)}" />
1349
1339
  <input type="hidden" name="code_challenge" value="${escapeHtml(input.codeChallenge)}" />
1350
1340
  <input type="hidden" name="code_challenge_method" value="${escapeHtml(input.codeChallengeMethod)}" />
1341
+ <input type="hidden" name="next" value="${escapeHtml(input.authorizeUrl)}" />
1351
1342
  <div class="actions">
1352
1343
  <button type="submit" name="decision" value="deny">Cancel</button>
1353
1344
  <button type="submit" name="decision" value="approve">Authorize</button>
@@ -1 +1 @@
1
- import{r as c,j as e}from"./vendor-react-CKJs5o3c.js";import{s as Ue,p as _,u as ie,v as De,w as se,x as le,y as we,t as r,E as Te,z as Me,B as ke,C as fe,F as b,G as oe}from"./index-CdlMnBxP.js";import{a0 as Re,x as $e,h as Ee,B as ne,m as C,b as Le,$ as Be,a5 as Ge}from"./vendor-icons-CLnehDTw.js";import"./vendor-markdown-BUxTU7dS.js";import"./vendor-dnd-DRzYolkg.js";import"./vendor-router-BbWMxlnO.js";function Fe(l){return typeof l.avatarUrl=="string"&&l.avatarUrl.trim().length>0}function K(l){return l?fe(l.avatarUrl,l.avatarRevision,l.avatarUpdatedAt):""}function ze(l){return l?fe(l.avatarSourceUrl,l.avatarRevision,l.avatarUpdatedAt):""}function He(l){return l.find(Fe)||l[0]}function ce(l){return`${l.seatScope||"unknown"}:${l.name.trim().toLowerCase()}`}function Oe(l){const m=Math.max(0,l-1);return`${m} duplicate${m===1?"":"s"}`}function de(l){return!l||typeof l!="object"?null:{used:Math.max(0,Number(l.used||0)),limit:l.limit===null||l.limit===void 0?null:Math.max(0,Number(l.limit||0)),remaining:l.remaining===null||l.remaining===void 0?null:Math.max(0,Number(l.remaining||0))}}function pe(l,m){const U=m?.variant==="inline"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconInline}`:m?.variant==="detail"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconDetail}`:r.aiProfileSeatScopeIcon;return l==="cloud_metered"?e.jsx("span",{className:`${U} ${r.aiProfileSeatScopeIconCloud}`,title:"Cloud MCP","aria-label":"Cloud MCP",children:e.jsx(Be,{size:20})}):l==="local_unmetered"?e.jsx("span",{className:`${U} ${r.aiProfileSeatScopeIconLocal}`,title:"Local MCP","aria-label":"Local MCP",children:e.jsx(Ge,{size:20})}):null}function qe({workspaceId:l,cloudAuthConfigured:m=!1,authSessionResolved:U=!1,isAuthenticated:ue=!1,cloudAiProfileSeatUsage:me=null,agentTrayOpen:E=!1,onCloseAgentTray:he,mcpSettingsNode:V=null}){const[S,L]=c.useState([]),[ve,ge]=c.useState(null),[B,J]=c.useState(!1),[h,D]=c.useState(null),[w,v]=c.useState(null),[T,x]=c.useState(null),[W,G]=c.useState(null),[ye,Y]=c.useState(!1),[P,j]=c.useState(null),[q,Q]=c.useState(null),[X,M]=c.useState(!1),[Pe,g]=c.useState(null),k=c.useCallback(async()=>{if(l){J(!0);try{const a=`/api/taskforce/workspace/assignee-options?workspaceId=${encodeURIComponent(l)}&kind=agent`,t=await fetch(a,{credentials:"include"}),i=t.ok?await t.json().catch(()=>({})):{},o=de(i?.aiProfileSeatUsage),d=Array.isArray(i?.assignees)?i.assignees.filter(s=>s.kind==="agent").map(s=>({id:String(s.value||""),name:String(s.label||s.value||"Unknown Agent"),username:String(s.username||s.value||""),icon:String(s.icon||"Bot"),color:String(s.color||"#6B7280"),avatarUrl:typeof s.avatarUrl=="string"?s.avatarUrl:null,avatarSourceUrl:typeof s.avatarSourceUrl=="string"?s.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(s.avatarRevision))?Math.max(0,Math.floor(Number(s.avatarRevision))):0,avatarUpdatedAt:typeof s.avatarUpdatedAt=="string"?s.avatarUpdatedAt:null,kind:String(s.kind||"agent"),description:typeof s.description=="string"?s.description:null,role:typeof s.role=="string"?s.role:null,provider:typeof s.provider=="string"?s.provider:null,model:typeof s.model=="string"?s.model:null,surfaceType:_(s.surfaceType),seatScope:Ue(s.seatScope),archivedAt:typeof s.archivedAt=="string"?s.archivedAt:null,createdAt:String(s.createdAt||""),updatedAt:String(s.updatedAt||s.createdAt||""),lastActiveAt:typeof s.lastActiveAt=="string"?s.lastActiveAt:null})):[];L(d),ge(o),x(s=>s&&!d.some(p=>p.id===s.profileId)?null:s),G(s=>s&&!d.some(p=>p.id===s.profileId)?null:s)}finally{J(!1)}}},[l]);c.useEffect(()=>{k()},[k]);const R=c.useMemo(()=>{const a=new Map;for(const t of S){const i=ie(t.surfaceType);a.has(i)||a.set(i,new Map);const o=a.get(i),d=ce(t);o.has(d)||o.set(d,[]),o.get(d).push(t)}return De.map(t=>({section:t,label:se(t),groups:Array.from(a.get(t)?.entries()||[]).map(([i,o])=>({groupId:`${t}:${i}`,section:t,sectionLabel:se(t),profiles:o,primaryProfile:He(o)}))})).filter(t=>t.groups.length>0)},[S]),y=c.useMemo(()=>R.flatMap(a=>a.groups),[R]),n=c.useMemo(()=>y.find(a=>a.groupId===P)||y[0]||null,[y,P]),u=c.useMemo(()=>S.find(a=>a.id===q)||null,[S,q]);c.useEffect(()=>{if(!y.length){P!==null&&j(null);return}(!P||!y.some(a=>a.groupId===P))&&j(y[0].groupId)},[y,P]);const Ae=async(a,t)=>{v(null),x(null);try{const i=await fetch("/api/taskforce/workspace/ai-profiles/merge",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({keepId:a,mergeId:t})}),o=await i.json().catch(()=>({}));if(!i.ok){v({type:"error",message:String(o?.error||"Failed to merge AI profiles.")});return}D(null),v({type:"success",message:"AI profiles merged."}),await k(),b({workspaceId:l,profileId:a,reason:"merge"})}catch{v({type:"error",message:"Failed to merge AI profiles."})}},Z=async a=>{if(window.confirm(`Remove ${a.name} from the active roster? This frees an AI profile seat and preserves task and comment history.`)){x(null),v(null);try{const i=await fetch("/api/taskforce/workspace/ai-profiles/archive",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a.id,reason:"manual_archive"})}),o=await i.json().catch(()=>({}));if(!i.ok){x({profileId:a.id,message:String(o?.error||"Failed to remove AI profile from roster.")});return}(h?.keepId===a.id||h?.mergeId===a.id)&&D(null),v({type:"success",message:"AI profile removed from active roster."}),await k(),b({workspaceId:l,profileId:a.id,reason:"archive"})}catch{x({profileId:a.id,message:"Failed to remove AI profile from roster."})}}},Se=async(a,t)=>{const i=_(t),o=new Set(a.profiles.map(p=>p.surfaceType??"")),d=i??"";if(o.size===1&&o.has(d))return;Y(!0),G(null),v(null);const s=[];try{for(const f of a.profiles){const I=await fetch("/api/taskforce/workspace/ai-profiles/surface-type",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:f.id,surfaceType:i})}),H=await I.json().catch(()=>({}));if(!I.ok||!H?.profile)throw new Error(String(H?.error||"Failed to update AI profile category."));const O=H.profile;s.push({...f,surfaceType:_(O.surfaceType),updatedAt:typeof O.updatedAt=="string"?O.updatedAt:f.updatedAt})}const p=new Map(s.map(f=>[f.id,f]));L(f=>f.map(I=>p.get(I.id)||I));const A=p.get(a.primaryProfile.id)||{...a.primaryProfile,surfaceType:i},Ce=ie(A.surfaceType);j(`${Ce}:${ce(A)}`),v({type:"success",message:"AI profile category updated."});for(const f of s)b({workspaceId:l,profileId:f.id,reason:"update"})}catch(p){G({profileId:a.primaryProfile.id,message:String(p?.message||"Failed to update AI profile category.")})}finally{Y(!1)}},ee=a=>new Promise((t,i)=>{const o=new FileReader;o.onload=()=>t(String(o.result||"")),o.onerror=()=>i(new Error("Failed to read image file.")),o.readAsDataURL(a)}),ae=a=>{const t=String(a?.id||"").trim();t&&L(i=>i.map(o=>o.id===t?{...o,avatarUrl:typeof a.avatarUrl=="string"?a.avatarUrl:null,avatarSourceUrl:typeof a.avatarSourceUrl=="string"?a.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(a.avatarRevision))?Math.max(0,Math.floor(Number(a.avatarRevision))):o.avatarRevision,avatarUpdatedAt:typeof a.avatarUpdatedAt=="string"?a.avatarUpdatedAt:o.avatarUpdatedAt,updatedAt:typeof a.updatedAt=="string"?a.updatedAt:o.updatedAt}:o))},xe=async(a,t,i)=>{const o=await ee(t),d=i?await ee(i):null,s={profileId:a,displayImage:{dataUrl:o,mimeType:t.type||"application/octet-stream",originalName:t.name||"display-avatar"}};i&&d&&(s.sourceImage={dataUrl:d,mimeType:i.type||"application/octet-stream",originalName:i.name||"source-avatar"});const p=await fetch("/api/taskforce/workspace/ai-profiles/avatar/upload",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}),A=await p.json().catch(()=>({}));if(!p.ok||!A?.profile)throw new Error(String(A?.error||"Failed to update AI profile avatar."));ae(A.profile),b({workspaceId:l,profileId:a,reason:"avatar"})},je=async a=>{const t=await fetch("/api/taskforce/workspace/ai-profiles/avatar",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a,avatarUrl:null,avatarSourceUrl:null})}),i=await t.json().catch(()=>({}));if(!t.ok||!i?.profile)throw new Error(String(i?.error||"Failed to update AI profile avatar."));ae(i.profile),b({workspaceId:l,profileId:a,reason:"avatar"})},Ne=async(a,t)=>{if(!u)return!1;if(!a.type.startsWith("image/"))return g("AI profile photo must be an image file."),!1;M(!0),g(null);try{const i=await oe(a,{maxBytes:5242880});if(i.exceededLimit)throw new Error(a.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile photo must be 5 MB or smaller.");const o=i.file;let d=null;if(t){const s=await oe(t,{maxBytes:5242880});if(s.exceededLimit)throw new Error(t.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile source photo must be 5 MB or smaller.");d=s.file}return await xe(u.id,o,d),!0}catch(i){return g(String(i?.message||"Failed to update AI profile photo.")),!1}finally{M(!1)}},Ie=async()=>{if(u){M(!0),g(null);try{await je(u.id)}catch(a){g(String(a?.message||"Failed to remove AI profile photo."))}finally{M(!1)}}},N=a=>{const t=String(a||"").trim();if(!t)return"Unknown";const i=Date.parse(t);return Number.isFinite(i)?new Date(i).toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"}):t},re=c.useMemo(()=>{if(!n)return{attributes:[],dates:[]};const a=n.primaryProfile,t=i=>i||"Not set";return{attributes:[{label:"Category",value:a.surfaceType?le(a.surfaceType):""},{label:"Connection",value:a.seatScope?we(a.seatScope):""},{label:"Provider",value:a.provider||""},{label:"Model",value:a.model||""}].map(i=>({...i,value:t(i.value)})),dates:[{label:"Status",value:a.archivedAt?"Retired":"Active"},{label:"Recruited",value:N(a.createdAt)},{label:"Last updated",value:N(a.updatedAt)},{label:"Last active",value:N(a.lastActiveAt)}].map(i=>({...i,value:t(i.value)}))}},[n]),be=!!n&&n.profiles.length>1,te=m&&U&&!ue,$=m?de(me):ve,F=te?"Log into account for Cloud agents":$?`${$.used}/${$.limit===null?"Unlimited":$.limit}`:null,z=!!V;return e.jsxs("section",{className:`${r.agentsModuleRoot} ${z?r.agentsModuleWithTray:""} ${z&&E?r.agentsModuleTrayOpen:""}`.trim(),children:[z&&e.jsxs("aside",{className:`${r.agentTrayPanel} ${E?r.agentTrayPanelOpen:""}`.trim(),"aria-label":"Agent MCP settings tray","aria-hidden":!E,children:[e.jsxs("div",{className:r.agentTrayHeader,children:[e.jsxs("span",{className:r.agentTrayTitle,children:[e.jsx(Re,{size:14}),"MCP Settings"]}),e.jsx("button",{type:"button",className:"tf-control-icon",onClick:he,title:"Collapse agent tray","aria-label":"Collapse agent tray",children:e.jsx($e,{size:16})})]}),e.jsx("div",{className:`${r.agentTrayContent} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.agentTrayContentInner,children:V})})]}),e.jsx("div",{className:r.agentsModuleContent,children:e.jsx("div",{className:`${r.settingGroup} ${r.agentsModuleGroup}`,children:e.jsxs("div",{children:[e.jsx("h4",{className:r.settingSubTitle,children:"Registered AI Profiles"}),e.jsx("p",{className:`${r.settingsHint} ${r.marginBottom12}`,children:"AI agents register profiles when connecting via MCP. Merge duplicates created when a token was lost, or remove inactive profiles from the active roster to free seats while preserving history."}),F&&e.jsx("div",{className:r.aiProfileSeatSummary,children:te?F:`Registered Cloud Agents: ${F}`}),B&&e.jsxs("div",{className:r.settingsHint,children:[e.jsx(Ee,{size:13,className:r.spinner})," Loading profiles…"]}),!B&&S.length===0&&e.jsx("div",{className:r.settingsHint,children:"No AI profiles registered yet."}),!B&&R.length>0&&e.jsxs("div",{className:r.aiProfilesExplorer,children:[e.jsx("div",{className:`${r.aiProfilesListPane} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.aiProfilesList,children:R.map(a=>e.jsxs("div",{className:r.aiProfilesSection,children:[e.jsx("div",{className:r.aiProfilesSectionHeader,children:a.label}),a.groups.map(t=>{const i=n?.groupId===t.groupId,o=t.primaryProfile;return e.jsxs("div",{role:"button",tabIndex:0,className:`${r.aiProfileGroup} ${t.profiles.length>1?r.aiProfileGroupDuplicate:""} ${i?r.aiProfileGroupSelected:""}`,onClick:()=>j(t.groupId),onKeyDown:d=>{d.key!=="Enter"&&d.key!==" "||(d.preventDefault(),j(t.groupId))},"aria-pressed":i,children:[pe(o.seatScope),e.jsxs("div",{className:r.aiProfileGroupHeader,children:[e.jsx("span",{className:r.aiProfileGroupAvatar,style:{color:o.color},children:o.avatarUrl?e.jsx("img",{src:K(o),alt:""}):e.jsx(ne,{size:22})}),e.jsxs("span",{className:r.aiProfileGroupIdentity,children:[e.jsx("span",{className:r.aiProfileName,children:o.name}),e.jsxs("span",{className:r.aiProfileHandle,children:["@",o.username]}),e.jsx("span",{className:r.aiProfileRole,children:o.role||"Role not set"}),t.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDuplicateBadge,children:[e.jsx(C,{size:11})," ",Oe(t.profiles.length)]})]})]})]},t.groupId)})]},a.section))})}),n&&e.jsx("div",{className:r.aiProfileDetailPane,children:e.jsxs("div",{className:r.aiProfileDetailCard,children:[pe(n.primaryProfile.seatScope,{variant:"detail"}),e.jsxs("div",{className:r.aiProfileDetailHero,children:[e.jsx(Te,{label:"Edit AI profile photo",imageUrl:K(n.primaryProfile),fallback:e.jsx(ne,{size:38}),accentColor:n.primaryProfile.color,size:176,width:153,height:207,radius:6,editBadgeSize:28,editIconSize:14,className:r.aiProfileDetailAvatar,onClick:()=>{g(null),Q(n.primaryProfile.id)}}),e.jsxs("div",{className:r.aiProfileDetailHeading,children:[e.jsx("div",{className:r.aiProfileDetailTitleRow,children:e.jsx("h5",{className:r.aiProfileDetailTitle,children:n.primaryProfile.name})}),e.jsxs("div",{className:r.aiProfileDetailMetaRow,children:[e.jsxs("span",{className:r.aiProfileDetailHandle,children:["@",n.primaryProfile.username]}),n.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDetailLinkedCount,children:[n.profiles.length," linked"]})]}),e.jsxs("div",{className:r.aiProfileDetailRole,children:["Role: ",n.primaryProfile.role||"Not set"]}),n.primaryProfile.description&&e.jsx("div",{className:r.aiProfileDetailDescription,children:n.primaryProfile.description}),e.jsxs("div",{className:r.aiProfileSignatureColor,children:[e.jsx("span",{children:"Signature color"}),e.jsx("span",{className:r.aiProfileSignatureSwatch,style:{backgroundColor:n.primaryProfile.color},"aria-hidden":"true"}),e.jsx("span",{children:n.primaryProfile.color})]})]})]}),e.jsxs("div",{className:r.aiProfileDetailDataList,children:[e.jsx("div",{className:r.aiProfileDetailDataGroup,children:re.attributes.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Category"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent category",value:n.primaryProfile.surfaceType??"",disabled:ye,onChange:t=>{Se(n,t.target.value)},children:[e.jsx("option",{value:"",children:"Unclassified"}),Me.map(t=>e.jsx("option",{value:t,children:le(t)},t))]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))}),e.jsx("div",{className:`${r.aiProfileDetailDataGroup} ${r.aiProfileDetailDateGroup}`,children:re.dates.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Status"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent roster status",value:n.primaryProfile.archivedAt?"retired":"active",onChange:t=>{t.target.value==="retire"&&Z(n.primaryProfile)},children:[e.jsx("option",{value:"active",children:"Active"}),n.primaryProfile.archivedAt?e.jsx("option",{value:"retired",children:"Retired"}):e.jsx("option",{value:"retire",children:"Retire from roster"})]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))})]}),W?.profileId===n.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:W.message})]}),be?e.jsxs("div",{className:r.aiProfileInstanceSection,children:[e.jsxs("div",{className:r.aiProfileInstanceSectionHeader,children:[e.jsx("span",{children:"Profile instances"}),e.jsx("span",{className:r.settingsHint,children:"Choose a keeper here if duplicates need to be merged."})]}),e.jsx("div",{className:r.aiProfileInstanceList,children:n.profiles.map(a=>e.jsxs("div",{className:r.aiProfileInstanceCard,children:[e.jsxs("div",{className:r.aiProfileInstanceTopRow,children:[e.jsxs("div",{children:[e.jsxs("div",{className:r.aiProfileInstanceName,children:["@",a.username]}),e.jsx("div",{className:r.aiProfileIdChip,children:a.id})]}),h?.keepId===a.id&&e.jsx("span",{className:r.aiProfileKeepBadge,children:"Keeping"})]}),e.jsxs("div",{className:r.aiProfileInstanceMeta,children:[e.jsxs("span",{children:["Created ",N(a.createdAt)]}),e.jsxs("span",{children:["Updated ",N(a.updatedAt)]})]}),h?.keepId!==a.id&&e.jsx("button",{className:r.secondaryHeaderBtn,title:"Keep this profile, merge others into it",onClick:()=>{const t=n.profiles.find(i=>i.id!==a.id)?.id;t&&D({keepId:a.id,mergeId:t})},children:"Keep this"}),T?.profileId===a.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:T.message})]}),e.jsx("button",{className:r.aiProfileDangerTextButton,title:"Remove this profile from the active roster",onClick:()=>{Z(a)},children:"Remove from roster"})]},a.id))}),h&&n.profiles.some(a=>a.id===h.keepId)&&e.jsxs("div",{className:r.aiProfileMergeActions,children:[e.jsx("button",{className:r.dangerBtn,onClick:()=>{Ae(h.keepId,h.mergeId)},children:"Merge duplicates"}),e.jsx("button",{className:r.secondaryHeaderBtn,onClick:()=>D(null),children:"Cancel"})]})]}):e.jsxs("div",{className:r.aiProfileIdFooter,children:[e.jsx("div",{className:r.aiProfileIdFooterValue,children:n.primaryProfile.id}),T?.profileId===n.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:T.message})]})]})]})})]}),w&&e.jsxs("div",{className:`${w.type==="success"?r.successMessage:r.errorMessage} ${r.marginTop12}`,children:[w.type==="success"?e.jsx(Le,{size:14}):e.jsx(C,{size:14}),w.message]})]})})}),e.jsx(ke,{isOpen:!!u,theme:"dark",title:"Edit AI Profile Photo",currentImageUrl:K(u),editorImageUrl:ze(u),fallbackInitial:(u?.name||"AI").charAt(0).toUpperCase(),accept:"image/png,image/jpeg,image/webp,image/gif",busy:X,hasPendingImage:!1,canRemove:!!u?.avatarUrl,error:Pe,notice:null,onClose:()=>{X||(Q(null),g(null))},onApplyImage:Ne,onRemoveImage:Ie})]})}export{qe as AgentsModule};
1
+ import{r as c,j as e}from"./vendor-react-CKJs5o3c.js";import{s as Ue,p as _,u as ie,v as De,w as se,x as le,y as we,t as r,E as Te,z as Me,B as ke,C as fe,F as b,G as oe}from"./index-rB9GGUCz.js";import{a0 as Re,x as $e,h as Ee,B as ne,m as C,b as Le,$ as Be,a5 as Ge}from"./vendor-icons-CLnehDTw.js";import"./vendor-markdown-BUxTU7dS.js";import"./vendor-dnd-DRzYolkg.js";import"./vendor-router-BbWMxlnO.js";function Fe(l){return typeof l.avatarUrl=="string"&&l.avatarUrl.trim().length>0}function K(l){return l?fe(l.avatarUrl,l.avatarRevision,l.avatarUpdatedAt):""}function ze(l){return l?fe(l.avatarSourceUrl,l.avatarRevision,l.avatarUpdatedAt):""}function He(l){return l.find(Fe)||l[0]}function ce(l){return`${l.seatScope||"unknown"}:${l.name.trim().toLowerCase()}`}function Oe(l){const m=Math.max(0,l-1);return`${m} duplicate${m===1?"":"s"}`}function de(l){return!l||typeof l!="object"?null:{used:Math.max(0,Number(l.used||0)),limit:l.limit===null||l.limit===void 0?null:Math.max(0,Number(l.limit||0)),remaining:l.remaining===null||l.remaining===void 0?null:Math.max(0,Number(l.remaining||0))}}function pe(l,m){const U=m?.variant==="inline"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconInline}`:m?.variant==="detail"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconDetail}`:r.aiProfileSeatScopeIcon;return l==="cloud_metered"?e.jsx("span",{className:`${U} ${r.aiProfileSeatScopeIconCloud}`,title:"Cloud MCP","aria-label":"Cloud MCP",children:e.jsx(Be,{size:20})}):l==="local_unmetered"?e.jsx("span",{className:`${U} ${r.aiProfileSeatScopeIconLocal}`,title:"Local MCP","aria-label":"Local MCP",children:e.jsx(Ge,{size:20})}):null}function qe({workspaceId:l,cloudAuthConfigured:m=!1,authSessionResolved:U=!1,isAuthenticated:ue=!1,cloudAiProfileSeatUsage:me=null,agentTrayOpen:E=!1,onCloseAgentTray:he,mcpSettingsNode:V=null}){const[S,L]=c.useState([]),[ve,ge]=c.useState(null),[B,J]=c.useState(!1),[h,D]=c.useState(null),[w,v]=c.useState(null),[T,x]=c.useState(null),[W,G]=c.useState(null),[ye,Y]=c.useState(!1),[P,j]=c.useState(null),[q,Q]=c.useState(null),[X,M]=c.useState(!1),[Pe,g]=c.useState(null),k=c.useCallback(async()=>{if(l){J(!0);try{const a=`/api/taskforce/workspace/assignee-options?workspaceId=${encodeURIComponent(l)}&kind=agent`,t=await fetch(a,{credentials:"include"}),i=t.ok?await t.json().catch(()=>({})):{},o=de(i?.aiProfileSeatUsage),d=Array.isArray(i?.assignees)?i.assignees.filter(s=>s.kind==="agent").map(s=>({id:String(s.value||""),name:String(s.label||s.value||"Unknown Agent"),username:String(s.username||s.value||""),icon:String(s.icon||"Bot"),color:String(s.color||"#6B7280"),avatarUrl:typeof s.avatarUrl=="string"?s.avatarUrl:null,avatarSourceUrl:typeof s.avatarSourceUrl=="string"?s.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(s.avatarRevision))?Math.max(0,Math.floor(Number(s.avatarRevision))):0,avatarUpdatedAt:typeof s.avatarUpdatedAt=="string"?s.avatarUpdatedAt:null,kind:String(s.kind||"agent"),description:typeof s.description=="string"?s.description:null,role:typeof s.role=="string"?s.role:null,provider:typeof s.provider=="string"?s.provider:null,model:typeof s.model=="string"?s.model:null,surfaceType:_(s.surfaceType),seatScope:Ue(s.seatScope),archivedAt:typeof s.archivedAt=="string"?s.archivedAt:null,createdAt:String(s.createdAt||""),updatedAt:String(s.updatedAt||s.createdAt||""),lastActiveAt:typeof s.lastActiveAt=="string"?s.lastActiveAt:null})):[];L(d),ge(o),x(s=>s&&!d.some(p=>p.id===s.profileId)?null:s),G(s=>s&&!d.some(p=>p.id===s.profileId)?null:s)}finally{J(!1)}}},[l]);c.useEffect(()=>{k()},[k]);const R=c.useMemo(()=>{const a=new Map;for(const t of S){const i=ie(t.surfaceType);a.has(i)||a.set(i,new Map);const o=a.get(i),d=ce(t);o.has(d)||o.set(d,[]),o.get(d).push(t)}return De.map(t=>({section:t,label:se(t),groups:Array.from(a.get(t)?.entries()||[]).map(([i,o])=>({groupId:`${t}:${i}`,section:t,sectionLabel:se(t),profiles:o,primaryProfile:He(o)}))})).filter(t=>t.groups.length>0)},[S]),y=c.useMemo(()=>R.flatMap(a=>a.groups),[R]),n=c.useMemo(()=>y.find(a=>a.groupId===P)||y[0]||null,[y,P]),u=c.useMemo(()=>S.find(a=>a.id===q)||null,[S,q]);c.useEffect(()=>{if(!y.length){P!==null&&j(null);return}(!P||!y.some(a=>a.groupId===P))&&j(y[0].groupId)},[y,P]);const Ae=async(a,t)=>{v(null),x(null);try{const i=await fetch("/api/taskforce/workspace/ai-profiles/merge",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({keepId:a,mergeId:t})}),o=await i.json().catch(()=>({}));if(!i.ok){v({type:"error",message:String(o?.error||"Failed to merge AI profiles.")});return}D(null),v({type:"success",message:"AI profiles merged."}),await k(),b({workspaceId:l,profileId:a,reason:"merge"})}catch{v({type:"error",message:"Failed to merge AI profiles."})}},Z=async a=>{if(window.confirm(`Remove ${a.name} from the active roster? This frees an AI profile seat and preserves task and comment history.`)){x(null),v(null);try{const i=await fetch("/api/taskforce/workspace/ai-profiles/archive",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a.id,reason:"manual_archive"})}),o=await i.json().catch(()=>({}));if(!i.ok){x({profileId:a.id,message:String(o?.error||"Failed to remove AI profile from roster.")});return}(h?.keepId===a.id||h?.mergeId===a.id)&&D(null),v({type:"success",message:"AI profile removed from active roster."}),await k(),b({workspaceId:l,profileId:a.id,reason:"archive"})}catch{x({profileId:a.id,message:"Failed to remove AI profile from roster."})}}},Se=async(a,t)=>{const i=_(t),o=new Set(a.profiles.map(p=>p.surfaceType??"")),d=i??"";if(o.size===1&&o.has(d))return;Y(!0),G(null),v(null);const s=[];try{for(const f of a.profiles){const I=await fetch("/api/taskforce/workspace/ai-profiles/surface-type",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:f.id,surfaceType:i})}),H=await I.json().catch(()=>({}));if(!I.ok||!H?.profile)throw new Error(String(H?.error||"Failed to update AI profile category."));const O=H.profile;s.push({...f,surfaceType:_(O.surfaceType),updatedAt:typeof O.updatedAt=="string"?O.updatedAt:f.updatedAt})}const p=new Map(s.map(f=>[f.id,f]));L(f=>f.map(I=>p.get(I.id)||I));const A=p.get(a.primaryProfile.id)||{...a.primaryProfile,surfaceType:i},Ce=ie(A.surfaceType);j(`${Ce}:${ce(A)}`),v({type:"success",message:"AI profile category updated."});for(const f of s)b({workspaceId:l,profileId:f.id,reason:"update"})}catch(p){G({profileId:a.primaryProfile.id,message:String(p?.message||"Failed to update AI profile category.")})}finally{Y(!1)}},ee=a=>new Promise((t,i)=>{const o=new FileReader;o.onload=()=>t(String(o.result||"")),o.onerror=()=>i(new Error("Failed to read image file.")),o.readAsDataURL(a)}),ae=a=>{const t=String(a?.id||"").trim();t&&L(i=>i.map(o=>o.id===t?{...o,avatarUrl:typeof a.avatarUrl=="string"?a.avatarUrl:null,avatarSourceUrl:typeof a.avatarSourceUrl=="string"?a.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(a.avatarRevision))?Math.max(0,Math.floor(Number(a.avatarRevision))):o.avatarRevision,avatarUpdatedAt:typeof a.avatarUpdatedAt=="string"?a.avatarUpdatedAt:o.avatarUpdatedAt,updatedAt:typeof a.updatedAt=="string"?a.updatedAt:o.updatedAt}:o))},xe=async(a,t,i)=>{const o=await ee(t),d=i?await ee(i):null,s={profileId:a,displayImage:{dataUrl:o,mimeType:t.type||"application/octet-stream",originalName:t.name||"display-avatar"}};i&&d&&(s.sourceImage={dataUrl:d,mimeType:i.type||"application/octet-stream",originalName:i.name||"source-avatar"});const p=await fetch("/api/taskforce/workspace/ai-profiles/avatar/upload",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}),A=await p.json().catch(()=>({}));if(!p.ok||!A?.profile)throw new Error(String(A?.error||"Failed to update AI profile avatar."));ae(A.profile),b({workspaceId:l,profileId:a,reason:"avatar"})},je=async a=>{const t=await fetch("/api/taskforce/workspace/ai-profiles/avatar",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a,avatarUrl:null,avatarSourceUrl:null})}),i=await t.json().catch(()=>({}));if(!t.ok||!i?.profile)throw new Error(String(i?.error||"Failed to update AI profile avatar."));ae(i.profile),b({workspaceId:l,profileId:a,reason:"avatar"})},Ne=async(a,t)=>{if(!u)return!1;if(!a.type.startsWith("image/"))return g("AI profile photo must be an image file."),!1;M(!0),g(null);try{const i=await oe(a,{maxBytes:5242880});if(i.exceededLimit)throw new Error(a.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile photo must be 5 MB or smaller.");const o=i.file;let d=null;if(t){const s=await oe(t,{maxBytes:5242880});if(s.exceededLimit)throw new Error(t.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile source photo must be 5 MB or smaller.");d=s.file}return await xe(u.id,o,d),!0}catch(i){return g(String(i?.message||"Failed to update AI profile photo.")),!1}finally{M(!1)}},Ie=async()=>{if(u){M(!0),g(null);try{await je(u.id)}catch(a){g(String(a?.message||"Failed to remove AI profile photo."))}finally{M(!1)}}},N=a=>{const t=String(a||"").trim();if(!t)return"Unknown";const i=Date.parse(t);return Number.isFinite(i)?new Date(i).toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"}):t},re=c.useMemo(()=>{if(!n)return{attributes:[],dates:[]};const a=n.primaryProfile,t=i=>i||"Not set";return{attributes:[{label:"Category",value:a.surfaceType?le(a.surfaceType):""},{label:"Connection",value:a.seatScope?we(a.seatScope):""},{label:"Provider",value:a.provider||""},{label:"Model",value:a.model||""}].map(i=>({...i,value:t(i.value)})),dates:[{label:"Status",value:a.archivedAt?"Retired":"Active"},{label:"Recruited",value:N(a.createdAt)},{label:"Last updated",value:N(a.updatedAt)},{label:"Last active",value:N(a.lastActiveAt)}].map(i=>({...i,value:t(i.value)}))}},[n]),be=!!n&&n.profiles.length>1,te=m&&U&&!ue,$=m?de(me):ve,F=te?"Log into account for Cloud agents":$?`${$.used}/${$.limit===null?"Unlimited":$.limit}`:null,z=!!V;return e.jsxs("section",{className:`${r.agentsModuleRoot} ${z?r.agentsModuleWithTray:""} ${z&&E?r.agentsModuleTrayOpen:""}`.trim(),children:[z&&e.jsxs("aside",{className:`${r.agentTrayPanel} ${E?r.agentTrayPanelOpen:""}`.trim(),"aria-label":"Agent MCP settings tray","aria-hidden":!E,children:[e.jsxs("div",{className:r.agentTrayHeader,children:[e.jsxs("span",{className:r.agentTrayTitle,children:[e.jsx(Re,{size:14}),"MCP Settings"]}),e.jsx("button",{type:"button",className:"tf-control-icon",onClick:he,title:"Collapse agent tray","aria-label":"Collapse agent tray",children:e.jsx($e,{size:16})})]}),e.jsx("div",{className:`${r.agentTrayContent} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.agentTrayContentInner,children:V})})]}),e.jsx("div",{className:r.agentsModuleContent,children:e.jsx("div",{className:`${r.settingGroup} ${r.agentsModuleGroup}`,children:e.jsxs("div",{children:[e.jsx("h4",{className:r.settingSubTitle,children:"Registered AI Profiles"}),e.jsx("p",{className:`${r.settingsHint} ${r.marginBottom12}`,children:"AI agents register profiles when connecting via MCP. Merge duplicates created when a token was lost, or remove inactive profiles from the active roster to free seats while preserving history."}),F&&e.jsx("div",{className:r.aiProfileSeatSummary,children:te?F:`Registered Cloud Agents: ${F}`}),B&&e.jsxs("div",{className:r.settingsHint,children:[e.jsx(Ee,{size:13,className:r.spinner})," Loading profiles…"]}),!B&&S.length===0&&e.jsx("div",{className:r.settingsHint,children:"No AI profiles registered yet."}),!B&&R.length>0&&e.jsxs("div",{className:r.aiProfilesExplorer,children:[e.jsx("div",{className:`${r.aiProfilesListPane} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.aiProfilesList,children:R.map(a=>e.jsxs("div",{className:r.aiProfilesSection,children:[e.jsx("div",{className:r.aiProfilesSectionHeader,children:a.label}),a.groups.map(t=>{const i=n?.groupId===t.groupId,o=t.primaryProfile;return e.jsxs("div",{role:"button",tabIndex:0,className:`${r.aiProfileGroup} ${t.profiles.length>1?r.aiProfileGroupDuplicate:""} ${i?r.aiProfileGroupSelected:""}`,onClick:()=>j(t.groupId),onKeyDown:d=>{d.key!=="Enter"&&d.key!==" "||(d.preventDefault(),j(t.groupId))},"aria-pressed":i,children:[pe(o.seatScope),e.jsxs("div",{className:r.aiProfileGroupHeader,children:[e.jsx("span",{className:r.aiProfileGroupAvatar,style:{color:o.color},children:o.avatarUrl?e.jsx("img",{src:K(o),alt:""}):e.jsx(ne,{size:22})}),e.jsxs("span",{className:r.aiProfileGroupIdentity,children:[e.jsx("span",{className:r.aiProfileName,children:o.name}),e.jsxs("span",{className:r.aiProfileHandle,children:["@",o.username]}),e.jsx("span",{className:r.aiProfileRole,children:o.role||"Role not set"}),t.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDuplicateBadge,children:[e.jsx(C,{size:11})," ",Oe(t.profiles.length)]})]})]})]},t.groupId)})]},a.section))})}),n&&e.jsx("div",{className:r.aiProfileDetailPane,children:e.jsxs("div",{className:r.aiProfileDetailCard,children:[pe(n.primaryProfile.seatScope,{variant:"detail"}),e.jsxs("div",{className:r.aiProfileDetailHero,children:[e.jsx(Te,{label:"Edit AI profile photo",imageUrl:K(n.primaryProfile),fallback:e.jsx(ne,{size:38}),accentColor:n.primaryProfile.color,size:176,width:153,height:207,radius:6,editBadgeSize:28,editIconSize:14,className:r.aiProfileDetailAvatar,onClick:()=>{g(null),Q(n.primaryProfile.id)}}),e.jsxs("div",{className:r.aiProfileDetailHeading,children:[e.jsx("div",{className:r.aiProfileDetailTitleRow,children:e.jsx("h5",{className:r.aiProfileDetailTitle,children:n.primaryProfile.name})}),e.jsxs("div",{className:r.aiProfileDetailMetaRow,children:[e.jsxs("span",{className:r.aiProfileDetailHandle,children:["@",n.primaryProfile.username]}),n.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDetailLinkedCount,children:[n.profiles.length," linked"]})]}),e.jsxs("div",{className:r.aiProfileDetailRole,children:["Role: ",n.primaryProfile.role||"Not set"]}),n.primaryProfile.description&&e.jsx("div",{className:r.aiProfileDetailDescription,children:n.primaryProfile.description}),e.jsxs("div",{className:r.aiProfileSignatureColor,children:[e.jsx("span",{children:"Signature color"}),e.jsx("span",{className:r.aiProfileSignatureSwatch,style:{backgroundColor:n.primaryProfile.color},"aria-hidden":"true"}),e.jsx("span",{children:n.primaryProfile.color})]})]})]}),e.jsxs("div",{className:r.aiProfileDetailDataList,children:[e.jsx("div",{className:r.aiProfileDetailDataGroup,children:re.attributes.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Category"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent category",value:n.primaryProfile.surfaceType??"",disabled:ye,onChange:t=>{Se(n,t.target.value)},children:[e.jsx("option",{value:"",children:"Unclassified"}),Me.map(t=>e.jsx("option",{value:t,children:le(t)},t))]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))}),e.jsx("div",{className:`${r.aiProfileDetailDataGroup} ${r.aiProfileDetailDateGroup}`,children:re.dates.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Status"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent roster status",value:n.primaryProfile.archivedAt?"retired":"active",onChange:t=>{t.target.value==="retire"&&Z(n.primaryProfile)},children:[e.jsx("option",{value:"active",children:"Active"}),n.primaryProfile.archivedAt?e.jsx("option",{value:"retired",children:"Retired"}):e.jsx("option",{value:"retire",children:"Retire from roster"})]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))})]}),W?.profileId===n.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:W.message})]}),be?e.jsxs("div",{className:r.aiProfileInstanceSection,children:[e.jsxs("div",{className:r.aiProfileInstanceSectionHeader,children:[e.jsx("span",{children:"Profile instances"}),e.jsx("span",{className:r.settingsHint,children:"Choose a keeper here if duplicates need to be merged."})]}),e.jsx("div",{className:r.aiProfileInstanceList,children:n.profiles.map(a=>e.jsxs("div",{className:r.aiProfileInstanceCard,children:[e.jsxs("div",{className:r.aiProfileInstanceTopRow,children:[e.jsxs("div",{children:[e.jsxs("div",{className:r.aiProfileInstanceName,children:["@",a.username]}),e.jsx("div",{className:r.aiProfileIdChip,children:a.id})]}),h?.keepId===a.id&&e.jsx("span",{className:r.aiProfileKeepBadge,children:"Keeping"})]}),e.jsxs("div",{className:r.aiProfileInstanceMeta,children:[e.jsxs("span",{children:["Created ",N(a.createdAt)]}),e.jsxs("span",{children:["Updated ",N(a.updatedAt)]})]}),h?.keepId!==a.id&&e.jsx("button",{className:r.secondaryHeaderBtn,title:"Keep this profile, merge others into it",onClick:()=>{const t=n.profiles.find(i=>i.id!==a.id)?.id;t&&D({keepId:a.id,mergeId:t})},children:"Keep this"}),T?.profileId===a.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:T.message})]}),e.jsx("button",{className:r.aiProfileDangerTextButton,title:"Remove this profile from the active roster",onClick:()=>{Z(a)},children:"Remove from roster"})]},a.id))}),h&&n.profiles.some(a=>a.id===h.keepId)&&e.jsxs("div",{className:r.aiProfileMergeActions,children:[e.jsx("button",{className:r.dangerBtn,onClick:()=>{Ae(h.keepId,h.mergeId)},children:"Merge duplicates"}),e.jsx("button",{className:r.secondaryHeaderBtn,onClick:()=>D(null),children:"Cancel"})]})]}):e.jsxs("div",{className:r.aiProfileIdFooter,children:[e.jsx("div",{className:r.aiProfileIdFooterValue,children:n.primaryProfile.id}),T?.profileId===n.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:T.message})]})]})]})})]}),w&&e.jsxs("div",{className:`${w.type==="success"?r.successMessage:r.errorMessage} ${r.marginTop12}`,children:[w.type==="success"?e.jsx(Le,{size:14}):e.jsx(C,{size:14}),w.message]})]})})}),e.jsx(ke,{isOpen:!!u,theme:"dark",title:"Edit AI Profile Photo",currentImageUrl:K(u),editorImageUrl:ze(u),fallbackInitial:(u?.name||"AI").charAt(0).toUpperCase(),accept:"image/png,image/jpeg,image/webp,image/gif",busy:X,hasPendingImage:!1,canRemove:!!u?.avatarUrl,error:Pe,notice:null,onClose:()=>{X||(Q(null),g(null))},onApplyImage:Ne,onRemoveImage:Ie})]})}export{qe as AgentsModule};