@geminilight/mindos 0.6.29 → 0.6.30
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/README.md +10 -4
- package/app/app/api/acp/config/route.ts +82 -0
- package/app/app/api/acp/detect/route.ts +71 -48
- package/app/app/api/acp/install/route.ts +51 -0
- package/app/app/api/acp/session/route.ts +141 -11
- package/app/app/api/ask/route.ts +116 -13
- package/app/app/api/workflows/route.ts +156 -0
- package/app/app/page.tsx +7 -2
- package/app/components/ActivityBar.tsx +12 -4
- package/app/components/AskModal.tsx +4 -1
- package/app/components/FileTree.tsx +21 -10
- package/app/components/HomeContent.tsx +1 -0
- package/app/components/Panel.tsx +1 -0
- package/app/components/RightAskPanel.tsx +5 -1
- package/app/components/SidebarLayout.tsx +6 -0
- package/app/components/agents/AgentDetailContent.tsx +263 -47
- package/app/components/agents/AgentsContentPage.tsx +11 -0
- package/app/components/agents/AgentsPanelA2aTab.tsx +285 -46
- package/app/components/agents/AgentsPanelSessionsTab.tsx +166 -0
- package/app/components/agents/agents-content-model.ts +2 -2
- package/app/components/ask/AgentSelectorCapsule.tsx +218 -0
- package/app/components/ask/AskContent.tsx +197 -239
- package/app/components/ask/FileChip.tsx +82 -17
- package/app/components/ask/MentionPopover.tsx +21 -3
- package/app/components/ask/MessageList.tsx +30 -9
- package/app/components/ask/SlashCommandPopover.tsx +21 -3
- package/app/components/panels/AgentsPanel.tsx +1 -0
- package/app/components/panels/AgentsPanelHubNav.tsx +9 -2
- package/app/components/panels/WorkflowsPanel.tsx +206 -0
- package/app/components/renderers/workflow-yaml/StepEditor.tsx +157 -0
- package/app/components/renderers/workflow-yaml/WorkflowEditor.tsx +201 -0
- package/app/components/renderers/workflow-yaml/WorkflowRunner.tsx +226 -0
- package/app/components/renderers/workflow-yaml/WorkflowYamlRenderer.tsx +126 -0
- package/app/components/renderers/workflow-yaml/execution.ts +177 -0
- package/app/components/renderers/workflow-yaml/index.ts +6 -0
- package/app/components/renderers/workflow-yaml/manifest.ts +21 -0
- package/app/components/renderers/workflow-yaml/parser.ts +172 -0
- package/app/components/renderers/workflow-yaml/selectors.tsx +522 -0
- package/app/components/renderers/workflow-yaml/serializer.ts +56 -0
- package/app/components/renderers/workflow-yaml/types.ts +46 -0
- package/app/hooks/useAcpConfig.ts +96 -0
- package/app/hooks/useAcpDetection.ts +69 -14
- package/app/hooks/useAcpRegistry.ts +46 -11
- package/app/hooks/useAskModal.ts +12 -5
- package/app/hooks/useAskPanel.ts +8 -5
- package/app/hooks/useAskSession.ts +19 -2
- package/app/hooks/useImageUpload.ts +152 -0
- package/app/lib/acp/acp-tools.ts +3 -1
- package/app/lib/acp/agent-descriptors.ts +274 -0
- package/app/lib/acp/bridge.ts +6 -0
- package/app/lib/acp/index.ts +20 -4
- package/app/lib/acp/registry.ts +74 -7
- package/app/lib/acp/session.ts +481 -28
- package/app/lib/acp/subprocess.ts +307 -21
- package/app/lib/acp/types.ts +158 -20
- package/app/lib/agent/model.ts +18 -3
- package/app/lib/agent/to-agent-messages.ts +25 -2
- package/app/lib/i18n/modules/knowledge.ts +4 -0
- package/app/lib/i18n/modules/navigation.ts +2 -0
- package/app/lib/i18n/modules/panels.ts +146 -2
- package/app/lib/pi-integration/skills.ts +21 -6
- package/app/lib/renderers/index.ts +2 -2
- package/app/lib/settings.ts +10 -0
- package/app/lib/types.ts +12 -1
- package/app/next-env.d.ts +1 -1
- package/app/package.json +3 -1
- package/package.json +1 -1
- package/templates/en/.mindos/workflows/Sprint Release.flow.yaml +130 -0
- package/templates/zh/.mindos/workflows//345/221/250/350/277/255/344/273/243/346/243/200/346/237/245.flow.yaml +84 -0
- package/app/components/renderers/workflow/WorkflowRenderer.tsx +0 -409
- package/app/components/renderers/workflow/manifest.ts +0 -14
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import Link from 'next/link';
|
|
4
4
|
import { useCallback, useMemo, useState } from 'react';
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
ArrowLeft, Activity, Globe, Key, Loader2, Server, Search,
|
|
7
|
+
Shield, ShieldCheck, Trash2, Wifi, WifiOff, Zap,
|
|
8
|
+
} from 'lucide-react';
|
|
7
9
|
import { useLocale } from '@/lib/LocaleContext';
|
|
8
10
|
import { toast } from '@/lib/toast';
|
|
9
11
|
import { useMcpData } from '@/hooks/useMcpData';
|
|
@@ -11,6 +13,7 @@ import { useA2aRegistry } from '@/hooks/useA2aRegistry';
|
|
|
11
13
|
import { apiFetch } from '@/lib/api';
|
|
12
14
|
import { copyToClipboard } from '@/lib/clipboard';
|
|
13
15
|
import { generateSnippet } from '@/lib/mcp-snippets';
|
|
16
|
+
import type { AgentInfo, McpStatus } from '../settings/types';
|
|
14
17
|
import {
|
|
15
18
|
aggregateCrossAgentMcpServers,
|
|
16
19
|
aggregateCrossAgentSkills,
|
|
@@ -43,7 +46,6 @@ export default function AgentDetailContent({ agentKey }: { agentKey: string }) {
|
|
|
43
46
|
const [confirmMcpRemove, setConfirmMcpRemove] = useState<string | null>(null);
|
|
44
47
|
const [mcpHint, setMcpHint] = useState<string | null>(null);
|
|
45
48
|
const [detailSkillName, setDetailSkillName] = useState<string | null>(null);
|
|
46
|
-
const [a2aOpen, setA2aOpen] = useState(false);
|
|
47
49
|
|
|
48
50
|
const filteredSkills = useMemo(
|
|
49
51
|
() =>
|
|
@@ -86,7 +88,11 @@ export default function AgentDetailContent({ agentKey }: { agentKey: string }) {
|
|
|
86
88
|
() => agent ? generateSnippet(agent, mcp.status, currentTransport) : { snippet: '', path: '' },
|
|
87
89
|
[agent, mcp.status, currentTransport],
|
|
88
90
|
);
|
|
89
|
-
const
|
|
91
|
+
const mindosSkillNames = useMemo(() => new Set(mcp.skills.map((s) => s.name)), [mcp.skills]);
|
|
92
|
+
const nativeInstalledSkills = useMemo(
|
|
93
|
+
() => (agent?.installedSkillNames ?? []).filter((n) => !mindosSkillNames.has(n)),
|
|
94
|
+
[agent?.installedSkillNames, mindosSkillNames],
|
|
95
|
+
);
|
|
90
96
|
const configuredMcpServers = agent?.configuredMcpServers ?? [];
|
|
91
97
|
|
|
92
98
|
|
|
@@ -361,48 +367,6 @@ export default function AgentDetailContent({ agentKey }: { agentKey: string }) {
|
|
|
361
367
|
</div>
|
|
362
368
|
</section>
|
|
363
369
|
|
|
364
|
-
{/* ═══════════ A2A CAPABILITIES ═══════════ */}
|
|
365
|
-
<section className="rounded-xl border border-border bg-card overflow-hidden">
|
|
366
|
-
<button
|
|
367
|
-
type="button"
|
|
368
|
-
onClick={() => setA2aOpen(!a2aOpen)}
|
|
369
|
-
className="w-full flex items-center justify-between gap-2 px-4 py-3 text-left hover:bg-muted/20 transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
370
|
-
aria-expanded={a2aOpen}
|
|
371
|
-
>
|
|
372
|
-
<h2 className="text-xs font-semibold text-foreground flex items-center gap-2 shrink-0">
|
|
373
|
-
<Globe size={12} className="text-muted-foreground/50" />
|
|
374
|
-
{p.a2aCapabilities}
|
|
375
|
-
</h2>
|
|
376
|
-
<ChevronDown
|
|
377
|
-
size={13}
|
|
378
|
-
className={cn('shrink-0 text-muted-foreground/50 transition-transform duration-200', a2aOpen && 'rotate-180')}
|
|
379
|
-
aria-hidden="true"
|
|
380
|
-
/>
|
|
381
|
-
</button>
|
|
382
|
-
<div
|
|
383
|
-
className={cn(
|
|
384
|
-
'grid transition-[grid-template-rows] duration-250 ease-out',
|
|
385
|
-
a2aOpen ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]',
|
|
386
|
-
)}
|
|
387
|
-
>
|
|
388
|
-
<div className="overflow-hidden" {...(!a2aOpen && { inert: true } as React.HTMLAttributes<HTMLDivElement>)}>
|
|
389
|
-
<div className="px-4 pb-3 pt-1 space-y-2 border-t border-border/40">
|
|
390
|
-
<div className="flex items-baseline gap-2 px-0.5 min-w-0">
|
|
391
|
-
<span className="text-2xs text-muted-foreground/50 uppercase tracking-wider shrink-0 min-w-[60px]">{p.a2aStatus}</span>
|
|
392
|
-
<span className={`text-xs font-medium ${
|
|
393
|
-
status === 'connected' ? 'text-[var(--success)]' : 'text-muted-foreground'
|
|
394
|
-
}`}>
|
|
395
|
-
{status === 'connected' ? p.a2aConnected : p.a2aUnavailable}
|
|
396
|
-
</span>
|
|
397
|
-
</div>
|
|
398
|
-
{a2a.agents.length === 0 && (
|
|
399
|
-
<p className="text-2xs text-muted-foreground/50">{p.a2aNoRemoteHint}</p>
|
|
400
|
-
)}
|
|
401
|
-
</div>
|
|
402
|
-
</div>
|
|
403
|
-
</div>
|
|
404
|
-
</section>
|
|
405
|
-
|
|
406
370
|
{/* ═══════════ SKILL ASSIGNMENTS ═══════════ */}
|
|
407
371
|
<section className="rounded-xl border border-border bg-card p-4 space-y-3">
|
|
408
372
|
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
@@ -518,7 +482,7 @@ export default function AgentDetailContent({ agentKey }: { agentKey: string }) {
|
|
|
518
482
|
<p className="text-sm text-muted-foreground">{a.detail.noSkills}</p>
|
|
519
483
|
)}
|
|
520
484
|
|
|
521
|
-
{/* Native installed skills
|
|
485
|
+
{/* Native installed skills */}
|
|
522
486
|
{nativeInstalledSkills.length > 0 && (
|
|
523
487
|
<div>
|
|
524
488
|
<p className="text-2xs font-medium text-muted-foreground uppercase tracking-wider mb-1.5">
|
|
@@ -543,6 +507,59 @@ export default function AgentDetailContent({ agentKey }: { agentKey: string }) {
|
|
|
543
507
|
{editError && <p className="text-xs text-error">{editError}</p>}
|
|
544
508
|
</section>
|
|
545
509
|
|
|
510
|
+
{/* ═══════════ A2A CAPABILITIES ═══════════ */}
|
|
511
|
+
<section className="rounded-xl border border-border bg-card p-4 space-y-3">
|
|
512
|
+
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
513
|
+
<h2 className="text-xs font-semibold text-foreground flex items-center gap-2 shrink-0">
|
|
514
|
+
<Globe size={12} className="text-muted-foreground/50" />
|
|
515
|
+
{p.a2aCapabilities}
|
|
516
|
+
</h2>
|
|
517
|
+
<span className={`text-2xs px-1.5 py-0.5 rounded font-medium shrink-0 ${
|
|
518
|
+
status === 'connected' ? 'bg-[var(--success)]/15 text-[var(--success)]' : 'bg-muted text-muted-foreground/60'
|
|
519
|
+
}`}>
|
|
520
|
+
{status === 'connected' ? p.a2aConnected : p.a2aUnavailable}
|
|
521
|
+
</span>
|
|
522
|
+
</div>
|
|
523
|
+
|
|
524
|
+
<div className="flex flex-wrap gap-x-6 gap-y-1 py-2 border-y border-border/30">
|
|
525
|
+
<DetailLine label={p.a2aStatus} value={status === 'connected' ? p.a2aConnected : p.a2aUnavailable} />
|
|
526
|
+
<DetailLine label={a.detail.transport} value={agent.transport ?? agent.preferredTransport} />
|
|
527
|
+
{a2a.agents.length > 0 && (
|
|
528
|
+
<DetailLine label="Remote agents" value={String(a2a.agents.length)} />
|
|
529
|
+
)}
|
|
530
|
+
</div>
|
|
531
|
+
|
|
532
|
+
{a2a.agents.length > 0 ? (
|
|
533
|
+
<div className="space-y-1">
|
|
534
|
+
{a2a.agents.map((remote) => (
|
|
535
|
+
<div key={remote.id} className="flex items-center gap-2.5 rounded-md px-2 py-1.5 hover:bg-muted/30 transition-colors duration-100">
|
|
536
|
+
<div className="w-6 h-6 rounded-md bg-muted/40 flex items-center justify-center shrink-0">
|
|
537
|
+
<Globe size={11} className="text-muted-foreground/60" />
|
|
538
|
+
</div>
|
|
539
|
+
<span className="text-xs text-foreground flex-1 min-w-0 truncate">{remote.card.name}</span>
|
|
540
|
+
{remote.reachable ? (
|
|
541
|
+
<Wifi size={11} className="text-[var(--success)] shrink-0" />
|
|
542
|
+
) : (
|
|
543
|
+
<WifiOff size={11} className="text-muted-foreground/50 shrink-0" />
|
|
544
|
+
)}
|
|
545
|
+
<span className="text-2xs text-muted-foreground/50 tabular-nums shrink-0">{remote.card.skills.length} skills</span>
|
|
546
|
+
</div>
|
|
547
|
+
))}
|
|
548
|
+
</div>
|
|
549
|
+
) : (
|
|
550
|
+
<p className="text-2xs text-muted-foreground/50">{p.a2aNoRemoteHint}</p>
|
|
551
|
+
)}
|
|
552
|
+
</section>
|
|
553
|
+
|
|
554
|
+
{/* ═══════════ RUNTIME & DIAGNOSTICS ═══════════ */}
|
|
555
|
+
<RuntimeDiagSection agent={agent} status={status} isMindOS={isMindOS} mcpStatus={mcp.status} />
|
|
556
|
+
|
|
557
|
+
{/* ═══════════ ENVIRONMENT & PERMISSIONS ═══════════ */}
|
|
558
|
+
<EnvPermSection agent={agent} isMindOS={isMindOS} />
|
|
559
|
+
|
|
560
|
+
{/* ═══════════ ACTIVITY & USAGE ═══════════ */}
|
|
561
|
+
<ActivitySection agent={agent} />
|
|
562
|
+
|
|
546
563
|
{/* ═══════════ Confirm Dialogs ═══════════ */}
|
|
547
564
|
<ConfirmDialog
|
|
548
565
|
open={confirmDelete !== null}
|
|
@@ -609,3 +626,202 @@ function formatRelativeTime(iso: string | undefined | null): string {
|
|
|
609
626
|
return iso;
|
|
610
627
|
}
|
|
611
628
|
}
|
|
629
|
+
|
|
630
|
+
/* ═══════════ Runtime & Diagnostics ═══════════ */
|
|
631
|
+
|
|
632
|
+
function RuntimeDiagSection({
|
|
633
|
+
agent,
|
|
634
|
+
status,
|
|
635
|
+
isMindOS,
|
|
636
|
+
mcpStatus,
|
|
637
|
+
}: {
|
|
638
|
+
agent: AgentInfo;
|
|
639
|
+
status: string;
|
|
640
|
+
isMindOS: boolean;
|
|
641
|
+
mcpStatus: McpStatus | null;
|
|
642
|
+
}) {
|
|
643
|
+
const { t } = useLocale();
|
|
644
|
+
const d = t.agentsContent.detail;
|
|
645
|
+
const [pingState, setPingState] = useState<'idle' | 'pinging' | 'ok' | 'fail'>('idle');
|
|
646
|
+
const [pingMs, setPingMs] = useState(0);
|
|
647
|
+
|
|
648
|
+
const handlePing = useCallback(async () => {
|
|
649
|
+
setPingState('pinging');
|
|
650
|
+
const start = performance.now();
|
|
651
|
+
try {
|
|
652
|
+
const res = await fetch('/api/mcp/status', { method: 'GET', signal: AbortSignal.timeout(5000) });
|
|
653
|
+
const elapsed = Math.round(performance.now() - start);
|
|
654
|
+
setPingMs(elapsed);
|
|
655
|
+
setPingState(res.ok ? 'ok' : 'fail');
|
|
656
|
+
} catch {
|
|
657
|
+
setPingState('fail');
|
|
658
|
+
}
|
|
659
|
+
}, []);
|
|
660
|
+
|
|
661
|
+
return (
|
|
662
|
+
<section className="rounded-xl border border-border bg-card p-4 space-y-3">
|
|
663
|
+
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
664
|
+
<h2 className="text-xs font-semibold text-foreground flex items-center gap-2 shrink-0">
|
|
665
|
+
<Activity size={12} className="text-muted-foreground/50" />
|
|
666
|
+
{d.runtimeDiagTitle}
|
|
667
|
+
</h2>
|
|
668
|
+
<button
|
|
669
|
+
type="button"
|
|
670
|
+
onClick={() => void handlePing()}
|
|
671
|
+
disabled={pingState === 'pinging'}
|
|
672
|
+
className="inline-flex items-center gap-1 px-2.5 py-1 text-2xs font-medium rounded-md border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
673
|
+
>
|
|
674
|
+
{pingState === 'pinging' ? (
|
|
675
|
+
<><Loader2 size={10} className="animate-spin" /> {d.runtimePinging}</>
|
|
676
|
+
) : (
|
|
677
|
+
<><Wifi size={10} /> {d.runtimePing}</>
|
|
678
|
+
)}
|
|
679
|
+
</button>
|
|
680
|
+
</div>
|
|
681
|
+
|
|
682
|
+
{pingState === 'ok' && (
|
|
683
|
+
<div role="status" className="rounded-md bg-[var(--success)]/10 border border-[var(--success)]/20 px-3 py-1.5 text-2xs text-[var(--success)] font-medium animate-in fade-in duration-200">
|
|
684
|
+
{d.runtimePingOk(pingMs)}
|
|
685
|
+
</div>
|
|
686
|
+
)}
|
|
687
|
+
{pingState === 'fail' && (
|
|
688
|
+
<div role="status" className="rounded-md bg-error/10 border border-error/20 px-3 py-1.5 text-2xs text-error font-medium animate-in fade-in duration-200">
|
|
689
|
+
{d.runtimePingFail}
|
|
690
|
+
</div>
|
|
691
|
+
)}
|
|
692
|
+
|
|
693
|
+
<div className="flex flex-wrap gap-x-6 gap-y-1 py-2 border-y border-border/30">
|
|
694
|
+
<DetailLine label={d.status} value={status} />
|
|
695
|
+
<DetailLine label={d.transport} value={agent.transport ?? agent.preferredTransport} />
|
|
696
|
+
{isMindOS && mcpStatus && (
|
|
697
|
+
<>
|
|
698
|
+
<DetailLine label={d.runtimeVersion} value={mcpStatus.endpoint} />
|
|
699
|
+
<DetailLine label={d.port} value={String(mcpStatus.port)} />
|
|
700
|
+
</>
|
|
701
|
+
)}
|
|
702
|
+
<DetailLine label={d.lastActivityAt} value={formatRelativeTime(agent.runtimeLastActivityAt)} />
|
|
703
|
+
{agent.runtimeConversationSignal !== undefined && (
|
|
704
|
+
<DetailLine label={d.conversationSignal} value={agent.runtimeConversationSignal ? 'Active' : 'Inactive'} />
|
|
705
|
+
)}
|
|
706
|
+
{agent.runtimeUsageSignal !== undefined && (
|
|
707
|
+
<DetailLine label={d.usageSignal} value={agent.runtimeUsageSignal ? 'Active' : 'Inactive'} />
|
|
708
|
+
)}
|
|
709
|
+
</div>
|
|
710
|
+
|
|
711
|
+
{!agent.runtimeLastActivityAt && pingState === 'idle' && (
|
|
712
|
+
<p className="text-2xs text-muted-foreground/50">{d.runtimeNoData}</p>
|
|
713
|
+
)}
|
|
714
|
+
</section>
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/* ═══════════ Environment & Permissions ═══════════ */
|
|
719
|
+
|
|
720
|
+
function EnvPermSection({
|
|
721
|
+
agent,
|
|
722
|
+
isMindOS,
|
|
723
|
+
}: {
|
|
724
|
+
agent: AgentInfo;
|
|
725
|
+
isMindOS: boolean;
|
|
726
|
+
}) {
|
|
727
|
+
const { t } = useLocale();
|
|
728
|
+
const d = t.agentsContent.detail;
|
|
729
|
+
|
|
730
|
+
const scope = agent.scope ?? (agent.hasProjectScope ? 'project' : agent.hasGlobalScope ? 'global' : '—');
|
|
731
|
+
const hasHiddenRoot = agent.hiddenRootPresent ?? false;
|
|
732
|
+
|
|
733
|
+
const permissions = [
|
|
734
|
+
{ label: d.envFileAccess, allowed: true },
|
|
735
|
+
{ label: d.envNetworkAccess, allowed: agent.transport === 'http' || isMindOS },
|
|
736
|
+
{ label: d.envWriteAccess, allowed: true },
|
|
737
|
+
{ label: d.envReadOnly, allowed: false },
|
|
738
|
+
];
|
|
739
|
+
|
|
740
|
+
return (
|
|
741
|
+
<section className="rounded-xl border border-border bg-card p-4 space-y-3">
|
|
742
|
+
<h2 className="text-xs font-semibold text-foreground flex items-center gap-2 shrink-0">
|
|
743
|
+
<Key size={12} className="text-muted-foreground/50" />
|
|
744
|
+
{d.envPermTitle}
|
|
745
|
+
</h2>
|
|
746
|
+
|
|
747
|
+
<div className="flex flex-wrap gap-x-6 gap-y-1 py-2 border-y border-border/30">
|
|
748
|
+
<DetailLine label={d.envScope} value={scope} />
|
|
749
|
+
<DetailLine label={d.format} value={agent.format} />
|
|
750
|
+
{agent.hiddenRootPath && (
|
|
751
|
+
<DetailLine label={d.hiddenRoot} value={agent.hiddenRootPath} />
|
|
752
|
+
)}
|
|
753
|
+
<DetailLine label={d.skillMode} value={agent.skillMode ?? '—'} />
|
|
754
|
+
</div>
|
|
755
|
+
|
|
756
|
+
<div className="space-y-1">
|
|
757
|
+
<p className="text-2xs font-medium text-muted-foreground/60 uppercase tracking-wider mb-1.5">
|
|
758
|
+
{d.envVars}
|
|
759
|
+
</p>
|
|
760
|
+
{agent.configuredMcpServers && agent.configuredMcpServers.length > 0 ? (
|
|
761
|
+
<p className="text-2xs text-muted-foreground">{d.envVarsCount(agent.configuredMcpServers.length)}</p>
|
|
762
|
+
) : (
|
|
763
|
+
<p className="text-2xs text-muted-foreground/50">{d.envVarsEmpty}</p>
|
|
764
|
+
)}
|
|
765
|
+
</div>
|
|
766
|
+
|
|
767
|
+
<div className="space-y-1.5">
|
|
768
|
+
<p className="text-2xs font-medium text-muted-foreground/60 uppercase tracking-wider">Permissions</p>
|
|
769
|
+
<div className="grid grid-cols-2 gap-1.5">
|
|
770
|
+
{permissions.map(({ label, allowed }) => (
|
|
771
|
+
<div key={label} className="flex items-center gap-1.5 rounded-md px-2 py-1.5 bg-muted/20">
|
|
772
|
+
{allowed ? (
|
|
773
|
+
<ShieldCheck size={12} className="text-[var(--success)] shrink-0" />
|
|
774
|
+
) : (
|
|
775
|
+
<Shield size={12} className="text-muted-foreground/40 shrink-0" />
|
|
776
|
+
)}
|
|
777
|
+
<span className="text-2xs text-foreground/80">{label}</span>
|
|
778
|
+
<span className={`ml-auto text-2xs font-medium ${allowed ? 'text-[var(--success)]' : 'text-muted-foreground/50'}`}>
|
|
779
|
+
{allowed ? d.envAllowed : d.envRestricted}
|
|
780
|
+
</span>
|
|
781
|
+
</div>
|
|
782
|
+
))}
|
|
783
|
+
</div>
|
|
784
|
+
</div>
|
|
785
|
+
</section>
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/* ═══════════ Activity & Usage ═══════════ */
|
|
790
|
+
|
|
791
|
+
function ActivitySection({ agent }: { agent: AgentInfo }) {
|
|
792
|
+
const { t } = useLocale();
|
|
793
|
+
const d = t.agentsContent.detail;
|
|
794
|
+
|
|
795
|
+
const hasActivity = !!agent.runtimeLastActivityAt;
|
|
796
|
+
|
|
797
|
+
return (
|
|
798
|
+
<section className="rounded-xl border border-border bg-card p-4 space-y-3">
|
|
799
|
+
<h2 className="text-xs font-semibold text-foreground flex items-center gap-2 shrink-0">
|
|
800
|
+
<Activity size={12} className="text-muted-foreground/50" />
|
|
801
|
+
{d.activityTitle}
|
|
802
|
+
</h2>
|
|
803
|
+
|
|
804
|
+
{hasActivity ? (
|
|
805
|
+
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
|
806
|
+
<StatCard label={d.activityLastInvocation} value={formatRelativeTime(agent.runtimeLastActivityAt)} />
|
|
807
|
+
<StatCard label={d.activityTotal} value={agent.runtimeConversationSignal ? 'Active' : '—'} />
|
|
808
|
+
<StatCard label={d.activityLast7d} value={agent.runtimeUsageSignal ? 'Active' : '—'} />
|
|
809
|
+
</div>
|
|
810
|
+
) : (
|
|
811
|
+
<div className="rounded-lg border border-dashed border-border/50 bg-muted/10 px-4 py-6 text-center">
|
|
812
|
+
<Activity size={20} className="text-muted-foreground/30 mx-auto mb-2" />
|
|
813
|
+
<p className="text-2xs text-muted-foreground/50">{d.activityNoData}</p>
|
|
814
|
+
</div>
|
|
815
|
+
)}
|
|
816
|
+
</section>
|
|
817
|
+
);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
function StatCard({ label, value }: { label: string; value: string }) {
|
|
821
|
+
return (
|
|
822
|
+
<div className="rounded-lg border border-border/40 bg-muted/10 px-3 py-2.5">
|
|
823
|
+
<p className="text-2xs text-muted-foreground/50 uppercase tracking-wider mb-0.5">{label}</p>
|
|
824
|
+
<p className="text-sm font-medium text-foreground font-mono tabular-nums">{value}</p>
|
|
825
|
+
</div>
|
|
826
|
+
);
|
|
827
|
+
}
|
|
@@ -16,6 +16,7 @@ import AgentsOverviewSection from './AgentsOverviewSection';
|
|
|
16
16
|
import AgentsMcpSection from './AgentsMcpSection';
|
|
17
17
|
import AgentsSkillsSection from './AgentsSkillsSection';
|
|
18
18
|
import AgentsPanelA2aTab from './AgentsPanelA2aTab';
|
|
19
|
+
import AgentsPanelSessionsTab from './AgentsPanelSessionsTab';
|
|
19
20
|
|
|
20
21
|
export default function AgentsContentPage({ tab }: { tab: AgentsDashboardTab }) {
|
|
21
22
|
const { t } = useLocale();
|
|
@@ -23,6 +24,12 @@ export default function AgentsContentPage({ tab }: { tab: AgentsDashboardTab })
|
|
|
23
24
|
const mcp = useMcpData();
|
|
24
25
|
const a2a = useA2aRegistry();
|
|
25
26
|
const pageHeader = useMemo(() => {
|
|
27
|
+
if (tab === 'sessions') {
|
|
28
|
+
return {
|
|
29
|
+
title: 'Sessions',
|
|
30
|
+
subtitle: 'Active ACP agent sessions.',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
26
33
|
if (tab === 'a2a') {
|
|
27
34
|
return {
|
|
28
35
|
title: a.navNetwork,
|
|
@@ -114,6 +121,10 @@ export default function AgentsContentPage({ tab }: { tab: AgentsDashboardTab })
|
|
|
114
121
|
onRemove={a2a.remove}
|
|
115
122
|
/>
|
|
116
123
|
)}
|
|
124
|
+
|
|
125
|
+
{tab === 'sessions' && (
|
|
126
|
+
<AgentsPanelSessionsTab />
|
|
127
|
+
)}
|
|
117
128
|
</div>
|
|
118
129
|
);
|
|
119
130
|
}
|