@contractspec/example.agent-console 1.46.0 → 1.47.0
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/.turbo/turbo-build$colon$bundle.log +275 -128
- package/.turbo/turbo-build.log +274 -127
- package/CHANGELOG.md +46 -0
- package/dist/agent/agent.entity.d.ts +36 -36
- package/dist/agent/agent.entity.d.ts.map +1 -1
- package/dist/agent/agent.enum.d.ts +4 -4
- package/dist/agent/agent.enum.d.ts.map +1 -1
- package/dist/agent/agent.event.d.ts +31 -31
- package/dist/agent/agent.event.d.ts.map +1 -1
- package/dist/agent/agent.event.js +5 -5
- package/dist/agent/agent.event.js.map +1 -1
- package/dist/agent/agent.handler.js.map +1 -1
- package/dist/agent/agent.operation.d.ts +117 -117
- package/dist/agent/agent.operation.d.ts.map +1 -1
- package/dist/agent/agent.presentation.d.ts +4 -5
- package/dist/agent/agent.presentation.d.ts.map +1 -1
- package/dist/agent/agent.presentation.js +7 -7
- package/dist/agent/agent.presentation.js.map +1 -1
- package/dist/agent/agent.schema.d.ts +95 -95
- package/dist/agent/agent.schema.d.ts.map +1 -1
- package/dist/agent/agent.test-spec.d.ts +8 -0
- package/dist/agent/agent.test-spec.d.ts.map +1 -0
- package/dist/agent/agent.test-spec.js +65 -0
- package/dist/agent/agent.test-spec.js.map +1 -0
- package/dist/agent.capability.d.ts +7 -0
- package/dist/agent.capability.d.ts.map +1 -0
- package/dist/agent.capability.js +20 -0
- package/dist/agent.capability.js.map +1 -0
- package/dist/agent.feature.d.ts.map +1 -1
- package/dist/agent.feature.js +4 -2
- package/dist/agent.feature.js.map +1 -1
- package/dist/example.d.ts +2 -2
- package/dist/example.d.ts.map +1 -1
- package/dist/example.js +4 -2
- package/dist/example.js.map +1 -1
- package/dist/handlers/agent.handlers.d.ts +135 -0
- package/dist/handlers/agent.handlers.d.ts.map +1 -0
- package/dist/handlers/agent.handlers.js +263 -0
- package/dist/handlers/agent.handlers.js.map +1 -0
- package/dist/handlers/index.d.ts +2 -1
- package/dist/handlers/index.js +2 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +19 -1
- package/dist/run/run.entity.d.ts +56 -56
- package/dist/run/run.enum.d.ts +5 -5
- package/dist/run/run.event.d.ts +71 -71
- package/dist/run/run.event.d.ts.map +1 -1
- package/dist/run/run.event.js +8 -8
- package/dist/run/run.event.js.map +1 -1
- package/dist/run/run.operation.d.ts +175 -175
- package/dist/run/run.operation.d.ts.map +1 -1
- package/dist/run/run.presentation.d.ts +3 -4
- package/dist/run/run.presentation.d.ts.map +1 -1
- package/dist/run/run.presentation.js +5 -5
- package/dist/run/run.presentation.js.map +1 -1
- package/dist/run/run.schema.d.ts +99 -99
- package/dist/run/run.test-spec.d.ts +8 -0
- package/dist/run/run.test-spec.d.ts.map +1 -0
- package/dist/run/run.test-spec.js +65 -0
- package/dist/run/run.test-spec.js.map +1 -0
- package/dist/seeders/index.d.ts +10 -0
- package/dist/seeders/index.d.ts.map +1 -0
- package/dist/seeders/index.js +20 -0
- package/dist/seeders/index.js.map +1 -0
- package/dist/shared/overlay-types.d.ts +34 -0
- package/dist/shared/overlay-types.d.ts.map +1 -0
- package/dist/shared/overlay-types.js +0 -0
- package/dist/tool/tool.entity.d.ts +24 -24
- package/dist/tool/tool.enum.d.ts +4 -4
- package/dist/tool/tool.event.d.ts +25 -25
- package/dist/tool/tool.event.js +4 -4
- package/dist/tool/tool.event.js.map +1 -1
- package/dist/tool/tool.handler.d.ts.map +1 -1
- package/dist/tool/tool.operation.d.ts +101 -101
- package/dist/tool/tool.presentation.d.ts +3 -4
- package/dist/tool/tool.presentation.d.ts.map +1 -1
- package/dist/tool/tool.presentation.js +5 -5
- package/dist/tool/tool.presentation.js.map +1 -1
- package/dist/tool/tool.schema.d.ts +52 -52
- package/dist/tool/tool.schema.d.ts.map +1 -1
- package/dist/tool/tool.test-spec.d.ts +8 -0
- package/dist/tool/tool.test-spec.d.ts.map +1 -0
- package/dist/tool/tool.test-spec.js +65 -0
- package/dist/tool/tool.test-spec.js.map +1 -0
- package/dist/ui/AgentDashboard.d.ts +7 -0
- package/dist/ui/AgentDashboard.d.ts.map +1 -0
- package/dist/ui/AgentDashboard.js +420 -0
- package/dist/ui/AgentDashboard.js.map +1 -0
- package/dist/ui/AgentRunList.d.ts +2 -0
- package/dist/ui/AgentRunList.js +5 -0
- package/dist/ui/AgentToolRegistry.d.ts +2 -0
- package/dist/ui/AgentToolRegistry.js +5 -0
- package/dist/ui/hooks/index.d.ts +6 -0
- package/dist/ui/hooks/index.js +8 -0
- package/dist/ui/hooks/useAgentList.d.ts +28 -0
- package/dist/ui/hooks/useAgentList.d.ts.map +1 -0
- package/dist/ui/hooks/useAgentList.js +66 -0
- package/dist/ui/hooks/useAgentList.js.map +1 -0
- package/dist/ui/hooks/useAgentMutations.d.ts +29 -0
- package/dist/ui/hooks/useAgentMutations.d.ts.map +1 -0
- package/dist/ui/hooks/useAgentMutations.js +124 -0
- package/dist/ui/hooks/useAgentMutations.js.map +1 -0
- package/dist/ui/hooks/useRunList.d.ts +24 -0
- package/dist/ui/hooks/useRunList.d.ts.map +1 -0
- package/dist/ui/hooks/useRunList.js +66 -0
- package/dist/ui/hooks/useRunList.js.map +1 -0
- package/dist/ui/hooks/useToolList.d.ts +40 -0
- package/dist/ui/hooks/useToolList.d.ts.map +1 -0
- package/dist/ui/hooks/useToolList.js +96 -0
- package/dist/ui/hooks/useToolList.js.map +1 -0
- package/dist/ui/index.d.ts +24 -0
- package/dist/ui/index.js +24 -0
- package/dist/ui/modals/AgentActionsModal.d.ts +27 -0
- package/dist/ui/modals/AgentActionsModal.d.ts.map +1 -0
- package/dist/ui/modals/AgentActionsModal.js +262 -0
- package/dist/ui/modals/AgentActionsModal.js.map +1 -0
- package/dist/ui/modals/CreateAgentModal.d.ts +25 -0
- package/dist/ui/modals/CreateAgentModal.d.ts.map +1 -0
- package/dist/ui/modals/CreateAgentModal.js +214 -0
- package/dist/ui/modals/CreateAgentModal.js.map +1 -0
- package/dist/ui/modals/index.d.ts +3 -0
- package/dist/ui/modals/index.js +4 -0
- package/dist/ui/overlays/demo-overlays.d.ts +19 -0
- package/dist/ui/overlays/demo-overlays.d.ts.map +1 -0
- package/dist/ui/overlays/demo-overlays.js +73 -0
- package/dist/ui/overlays/demo-overlays.js.map +1 -0
- package/dist/ui/overlays/index.d.ts +2 -0
- package/dist/ui/overlays/index.js +3 -0
- package/dist/ui/renderers/agent-list.markdown.d.ts +15 -0
- package/dist/ui/renderers/agent-list.markdown.d.ts.map +1 -0
- package/dist/ui/renderers/agent-list.markdown.js +51 -0
- package/dist/ui/renderers/agent-list.markdown.js.map +1 -0
- package/dist/ui/renderers/agent-list.renderer.d.ts +11 -0
- package/dist/ui/renderers/agent-list.renderer.d.ts.map +1 -0
- package/dist/ui/renderers/agent-list.renderer.js +19 -0
- package/dist/ui/renderers/agent-list.renderer.js.map +1 -0
- package/dist/ui/renderers/dashboard.markdown.d.ts +15 -0
- package/dist/ui/renderers/dashboard.markdown.d.ts.map +1 -0
- package/dist/ui/renderers/dashboard.markdown.js +100 -0
- package/dist/ui/renderers/dashboard.markdown.js.map +1 -0
- package/dist/ui/renderers/index.d.ts +6 -0
- package/dist/ui/renderers/index.js +7 -0
- package/dist/ui/renderers/run-list.markdown.d.ts +15 -0
- package/dist/ui/renderers/run-list.markdown.d.ts.map +1 -0
- package/dist/ui/renderers/run-list.markdown.js +44 -0
- package/dist/ui/renderers/run-list.markdown.js.map +1 -0
- package/dist/ui/renderers/tool-registry.markdown.d.ts +15 -0
- package/dist/ui/renderers/tool-registry.markdown.d.ts.map +1 -0
- package/dist/ui/renderers/tool-registry.markdown.js +55 -0
- package/dist/ui/renderers/tool-registry.markdown.js.map +1 -0
- package/dist/ui/views/AgentListView.d.ts +7 -0
- package/dist/ui/views/AgentListView.d.ts.map +1 -0
- package/dist/ui/views/AgentListView.js +93 -0
- package/dist/ui/views/AgentListView.js.map +1 -0
- package/dist/ui/views/RunListView.d.ts +14 -0
- package/dist/ui/views/RunListView.d.ts.map +1 -0
- package/dist/ui/views/RunListView.js +165 -0
- package/dist/ui/views/RunListView.js.map +1 -0
- package/dist/ui/views/ToolRegistryView.d.ts +14 -0
- package/dist/ui/views/ToolRegistryView.d.ts.map +1 -0
- package/dist/ui/views/ToolRegistryView.js +97 -0
- package/dist/ui/views/ToolRegistryView.js.map +1 -0
- package/dist/ui/views/index.d.ts +4 -0
- package/dist/ui/views/index.js +5 -0
- package/package.json +46 -10
- package/src/agent/agent.presentation.ts +7 -8
- package/src/agent/agent.test-spec.ts +55 -0
- package/src/agent.capability.ts +13 -0
- package/src/agent.feature.ts +3 -2
- package/src/example.ts +3 -3
- package/src/handlers/agent.handlers.ts +572 -0
- package/src/handlers/index.ts +3 -0
- package/src/index.ts +5 -0
- package/src/run/run.presentation.ts +5 -6
- package/src/run/run.test-spec.ts +55 -0
- package/src/seeders/index.ts +29 -0
- package/src/shared/overlay-types.ts +39 -0
- package/src/tool/tool.presentation.ts +5 -6
- package/src/tool/tool.test-spec.ts +55 -0
- package/src/ui/AgentDashboard.tsx +416 -0
- package/src/ui/AgentRunList.tsx +8 -0
- package/src/ui/AgentToolRegistry.tsx +8 -0
- package/src/ui/hooks/index.ts +14 -0
- package/src/ui/hooks/useAgentList.ts +80 -0
- package/src/ui/hooks/useAgentMutations.ts +156 -0
- package/src/ui/hooks/useRunList.ts +81 -0
- package/src/ui/hooks/useToolList.ts +122 -0
- package/src/ui/index.ts +21 -0
- package/src/ui/modals/AgentActionsModal.tsx +306 -0
- package/src/ui/modals/CreateAgentModal.tsx +257 -0
- package/src/ui/modals/index.ts +2 -0
- package/src/ui/overlays/demo-overlays.ts +77 -0
- package/src/ui/overlays/index.ts +1 -0
- package/src/ui/renderers/agent-list.markdown.ts +84 -0
- package/src/ui/renderers/agent-list.renderer.tsx +27 -0
- package/src/ui/renderers/dashboard.markdown.ts +169 -0
- package/src/ui/renderers/index.ts +12 -0
- package/src/ui/renderers/run-list.markdown.ts +75 -0
- package/src/ui/renderers/tool-registry.markdown.ts +91 -0
- package/src/ui/views/AgentListView.tsx +113 -0
- package/src/ui/views/RunListView.tsx +173 -0
- package/src/ui/views/ToolRegistryView.tsx +140 -0
- package/src/ui/views/index.ts +6 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface OverlayDefinition {
|
|
2
|
+
overlayId: string;
|
|
3
|
+
version: string;
|
|
4
|
+
description: string;
|
|
5
|
+
appliesTo: Record<string, string>;
|
|
6
|
+
modifications: OverlayModification[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type OverlayModification =
|
|
10
|
+
| HideFieldModification
|
|
11
|
+
| RenameLabelModification
|
|
12
|
+
| AddBadgeModification
|
|
13
|
+
| SetLimitModification;
|
|
14
|
+
|
|
15
|
+
export interface HideFieldModification {
|
|
16
|
+
type: 'hideField';
|
|
17
|
+
field: string;
|
|
18
|
+
reason?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RenameLabelModification {
|
|
22
|
+
type: 'renameLabel';
|
|
23
|
+
field: string;
|
|
24
|
+
newLabel: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AddBadgeModification {
|
|
28
|
+
type: 'addBadge';
|
|
29
|
+
position: 'header' | 'footer';
|
|
30
|
+
label: string;
|
|
31
|
+
variant: 'warning' | 'info' | 'error' | 'success' | 'default';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface SetLimitModification {
|
|
35
|
+
type: 'setLimit';
|
|
36
|
+
field: string;
|
|
37
|
+
max: number;
|
|
38
|
+
message: string;
|
|
39
|
+
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { StabilityEnum } from '@contractspec/lib.contracts';
|
|
1
|
+
import { StabilityEnum, definePresentation } from '@contractspec/lib.contracts';
|
|
3
2
|
import { ToolSummaryModel } from './tool.schema';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Presentation for displaying a list of tools.
|
|
7
6
|
*/
|
|
8
|
-
export const ToolListPresentation
|
|
7
|
+
export const ToolListPresentation = definePresentation({
|
|
9
8
|
meta: {
|
|
10
9
|
key: 'agent-console.tool.list',
|
|
11
10
|
version: '1.0.0',
|
|
@@ -27,12 +26,12 @@ export const ToolListPresentation: PresentationSpec = {
|
|
|
27
26
|
},
|
|
28
27
|
targets: ['react', 'markdown', 'application/json'],
|
|
29
28
|
policy: { flags: ['agent-console.enabled'] },
|
|
30
|
-
};
|
|
29
|
+
});
|
|
31
30
|
|
|
32
31
|
/**
|
|
33
32
|
* Presentation for tool detail view.
|
|
34
33
|
*/
|
|
35
|
-
export const ToolDetailPresentation
|
|
34
|
+
export const ToolDetailPresentation = definePresentation({
|
|
36
35
|
meta: {
|
|
37
36
|
key: 'agent-console.tool.detail',
|
|
38
37
|
version: '1.0.0',
|
|
@@ -53,4 +52,4 @@ export const ToolDetailPresentation: PresentationSpec = {
|
|
|
53
52
|
},
|
|
54
53
|
targets: ['react', 'markdown'],
|
|
55
54
|
policy: { flags: ['agent-console.enabled'] },
|
|
56
|
-
};
|
|
55
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { defineTestSpec } from '@contractspec/lib.contracts';
|
|
2
|
+
|
|
3
|
+
export const toolListTest = defineTestSpec({
|
|
4
|
+
meta: {
|
|
5
|
+
key: 'test.agent.tool.list',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
owners: ['@agent-console-team'],
|
|
8
|
+
description: 'Test for listing tools',
|
|
9
|
+
stability: 'stable',
|
|
10
|
+
tags: ['test'],
|
|
11
|
+
},
|
|
12
|
+
target: {
|
|
13
|
+
type: 'operation',
|
|
14
|
+
operation: { key: 'agent.tool.list', version: '1.0.0' },
|
|
15
|
+
},
|
|
16
|
+
scenarios: [
|
|
17
|
+
{
|
|
18
|
+
key: 'success',
|
|
19
|
+
when: { operation: { key: 'agent.tool.list' } },
|
|
20
|
+
then: [{ type: 'expectOutput', match: {} }],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
key: 'error',
|
|
24
|
+
when: { operation: { key: 'agent.tool.list' } },
|
|
25
|
+
then: [{ type: 'expectError' }],
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const toolGetTest = defineTestSpec({
|
|
31
|
+
meta: {
|
|
32
|
+
key: 'test.agent.tool.get',
|
|
33
|
+
version: '1.0.0',
|
|
34
|
+
owners: ['@agent-console-team'],
|
|
35
|
+
description: 'Test for getting tool',
|
|
36
|
+
stability: 'stable',
|
|
37
|
+
tags: ['test'],
|
|
38
|
+
},
|
|
39
|
+
target: {
|
|
40
|
+
type: 'operation',
|
|
41
|
+
operation: { key: 'agent.tool.get', version: '1.0.0' },
|
|
42
|
+
},
|
|
43
|
+
scenarios: [
|
|
44
|
+
{
|
|
45
|
+
key: 'success',
|
|
46
|
+
when: { operation: { key: 'agent.tool.get' } },
|
|
47
|
+
then: [{ type: 'expectOutput', match: {} }],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
key: 'error',
|
|
51
|
+
when: { operation: { key: 'agent.tool.get' } },
|
|
52
|
+
then: [{ type: 'expectError' }],
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
});
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Agent Console Dashboard
|
|
5
|
+
*
|
|
6
|
+
* Fully integrated with ContractSpec example handlers,
|
|
7
|
+
* design-system components, and command mutations.
|
|
8
|
+
*
|
|
9
|
+
* Commands wired:
|
|
10
|
+
* - CreateAgentCommand -> Create Agent button + modal
|
|
11
|
+
* - UpdateAgentCommand -> Status changes via modal
|
|
12
|
+
* - ExecuteAgentCommand -> Execute agent via modal
|
|
13
|
+
*/
|
|
14
|
+
import { useState, useMemo, useCallback } from 'react';
|
|
15
|
+
import {
|
|
16
|
+
StatCard,
|
|
17
|
+
StatCardGroup,
|
|
18
|
+
Button,
|
|
19
|
+
} from '@contractspec/lib.design-system';
|
|
20
|
+
// import { AgentListView } from './views/AgentListView';
|
|
21
|
+
import { RunListView } from './views/RunListView';
|
|
22
|
+
import { ToolRegistryView } from './views/ToolRegistryView';
|
|
23
|
+
import { useRunList, type RunMetrics } from './hooks/useRunList';
|
|
24
|
+
import { useAgentList, type Agent } from './hooks/useAgentList';
|
|
25
|
+
import { useAgentMutations } from './hooks/useAgentMutations';
|
|
26
|
+
import { CreateAgentModal } from './modals/CreateAgentModal';
|
|
27
|
+
import { AgentActionsModal } from './modals/AgentActionsModal';
|
|
28
|
+
|
|
29
|
+
type Tab = 'runs' | 'agents' | 'tools' | 'metrics';
|
|
30
|
+
|
|
31
|
+
export function AgentDashboard() {
|
|
32
|
+
const [activeTab, setActiveTab] = useState<Tab>('runs');
|
|
33
|
+
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
|
34
|
+
const [selectedAgent, setSelectedAgent] = useState<Agent | null>(null);
|
|
35
|
+
const [isAgentActionsOpen, setIsAgentActionsOpen] = useState(false);
|
|
36
|
+
|
|
37
|
+
const { metrics, refetch: refetchRuns } = useRunList();
|
|
38
|
+
const { refetch: refetchAgents } = useAgentList();
|
|
39
|
+
|
|
40
|
+
const mutations = useAgentMutations({
|
|
41
|
+
onSuccess: () => {
|
|
42
|
+
refetchAgents();
|
|
43
|
+
refetchRuns();
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const handleAgentClick = useCallback((agent: Agent) => {
|
|
48
|
+
setSelectedAgent(agent);
|
|
49
|
+
setIsAgentActionsOpen(true);
|
|
50
|
+
}, []);
|
|
51
|
+
|
|
52
|
+
const tabs: { id: Tab; label: string; icon: string }[] = [
|
|
53
|
+
{ id: 'runs', label: 'Runs', icon: '▶' },
|
|
54
|
+
{ id: 'agents', label: 'Agents', icon: '🤖' },
|
|
55
|
+
{ id: 'tools', label: 'Tools', icon: '🔧' },
|
|
56
|
+
{ id: 'metrics', label: 'Metrics', icon: '📊' },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
// Compute summary stats from metrics
|
|
60
|
+
const summaryStats = useMemo(() => {
|
|
61
|
+
if (!metrics) {
|
|
62
|
+
return [
|
|
63
|
+
{ label: 'Total Runs', value: '-', hint: 'Loading...' },
|
|
64
|
+
{ label: 'Success Rate', value: '-', hint: '' },
|
|
65
|
+
{ label: 'Total Tokens', value: '-', hint: '' },
|
|
66
|
+
{ label: 'Total Cost', value: '-', hint: '' },
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
return [
|
|
70
|
+
{
|
|
71
|
+
label: 'Total Runs',
|
|
72
|
+
value: metrics.totalRuns.toLocaleString(),
|
|
73
|
+
hint: `${(metrics.successRate * 100).toFixed(0)}% success`,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
label: 'Success Rate',
|
|
77
|
+
value: `${(metrics.successRate * 100).toFixed(0)}%`,
|
|
78
|
+
hint: 'of all runs',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
label: 'Total Tokens',
|
|
82
|
+
value:
|
|
83
|
+
metrics.totalTokens >= 1000000
|
|
84
|
+
? `${(metrics.totalTokens / 1000000).toFixed(1)}M`
|
|
85
|
+
: `${(metrics.totalTokens / 1000).toFixed(0)}K`,
|
|
86
|
+
hint: 'This period',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
label: 'Total Cost',
|
|
90
|
+
value: `$${metrics.totalCostUsd.toFixed(2)}`,
|
|
91
|
+
hint: 'This period',
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
}, [metrics]);
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className="space-y-6">
|
|
98
|
+
{/* Header */}
|
|
99
|
+
<div className="flex items-center justify-between">
|
|
100
|
+
<h2 className="text-2xl font-bold">AI Agent Console</h2>
|
|
101
|
+
<Button onPress={() => setIsCreateModalOpen(true)}>
|
|
102
|
+
<span className="mr-2">+</span> New Agent
|
|
103
|
+
</Button>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
{/* Summary Stats Row */}
|
|
107
|
+
<StatCardGroup>
|
|
108
|
+
{summaryStats.map((stat, i) => (
|
|
109
|
+
<StatCard
|
|
110
|
+
key={i}
|
|
111
|
+
label={stat.label}
|
|
112
|
+
value={stat.value}
|
|
113
|
+
hint={stat.hint}
|
|
114
|
+
/>
|
|
115
|
+
))}
|
|
116
|
+
</StatCardGroup>
|
|
117
|
+
|
|
118
|
+
{/* Navigation Tabs */}
|
|
119
|
+
<nav className="bg-muted flex gap-1 rounded-lg p-1" role="tablist">
|
|
120
|
+
{tabs.map((tab) => (
|
|
121
|
+
<button
|
|
122
|
+
key={tab.id}
|
|
123
|
+
type="button"
|
|
124
|
+
role="tab"
|
|
125
|
+
aria-selected={activeTab === tab.id}
|
|
126
|
+
onClick={() => setActiveTab(tab.id)}
|
|
127
|
+
className={`flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors ${
|
|
128
|
+
activeTab === tab.id
|
|
129
|
+
? 'bg-background text-foreground shadow-sm'
|
|
130
|
+
: 'text-muted-foreground hover:text-foreground'
|
|
131
|
+
}`}
|
|
132
|
+
>
|
|
133
|
+
<span>{tab.icon}</span>
|
|
134
|
+
{tab.label}
|
|
135
|
+
</button>
|
|
136
|
+
))}
|
|
137
|
+
</nav>
|
|
138
|
+
|
|
139
|
+
{/* Tab Content */}
|
|
140
|
+
<div className="min-h-[400px]" role="tabpanel">
|
|
141
|
+
{activeTab === 'runs' && <RunListView />}
|
|
142
|
+
{activeTab === 'agents' && (
|
|
143
|
+
<AgentListViewWithActions onAgentClick={handleAgentClick} />
|
|
144
|
+
)}
|
|
145
|
+
{activeTab === 'tools' && <ToolRegistryView />}
|
|
146
|
+
{activeTab === 'metrics' && <MetricsView metrics={metrics} />}
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
{/* Create Agent Modal */}
|
|
150
|
+
<CreateAgentModal
|
|
151
|
+
isOpen={isCreateModalOpen}
|
|
152
|
+
onClose={() => setIsCreateModalOpen(false)}
|
|
153
|
+
onSubmit={async (input) => {
|
|
154
|
+
await mutations.createAgent(input);
|
|
155
|
+
}}
|
|
156
|
+
isLoading={mutations.createState.loading}
|
|
157
|
+
/>
|
|
158
|
+
|
|
159
|
+
{/* Agent Actions Modal */}
|
|
160
|
+
<AgentActionsModal
|
|
161
|
+
isOpen={isAgentActionsOpen}
|
|
162
|
+
agent={selectedAgent}
|
|
163
|
+
onClose={() => {
|
|
164
|
+
setIsAgentActionsOpen(false);
|
|
165
|
+
setSelectedAgent(null);
|
|
166
|
+
}}
|
|
167
|
+
onActivate={async (agentId) => {
|
|
168
|
+
await mutations.activateAgent(agentId);
|
|
169
|
+
}}
|
|
170
|
+
onPause={async (agentId) => {
|
|
171
|
+
await mutations.pauseAgent(agentId);
|
|
172
|
+
}}
|
|
173
|
+
onArchive={async (agentId) => {
|
|
174
|
+
await mutations.archiveAgent(agentId);
|
|
175
|
+
}}
|
|
176
|
+
onExecute={async (agentId, message) => {
|
|
177
|
+
await mutations.executeAgent({ agentId, message });
|
|
178
|
+
}}
|
|
179
|
+
isLoading={mutations.isLoading}
|
|
180
|
+
/>
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Agent List View with click handler
|
|
187
|
+
*/
|
|
188
|
+
function AgentListViewWithActions({
|
|
189
|
+
onAgentClick,
|
|
190
|
+
}: {
|
|
191
|
+
onAgentClick: (agent: Agent) => void;
|
|
192
|
+
}) {
|
|
193
|
+
const { data, loading, error, stats, refetch } = useAgentList();
|
|
194
|
+
|
|
195
|
+
if (loading && !data) {
|
|
196
|
+
return (
|
|
197
|
+
<div className="text-muted-foreground flex h-64 items-center justify-center">
|
|
198
|
+
Loading agents...
|
|
199
|
+
</div>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (error) {
|
|
204
|
+
return (
|
|
205
|
+
<div className="text-destructive flex h-64 flex-col items-center justify-center">
|
|
206
|
+
<p>Failed to load agents: {error.message}</p>
|
|
207
|
+
<Button variant="outline" onPress={refetch} className="mt-2">
|
|
208
|
+
Retry
|
|
209
|
+
</Button>
|
|
210
|
+
</div>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!data?.items.length) {
|
|
215
|
+
return (
|
|
216
|
+
<div className="text-muted-foreground flex h-64 flex-col items-center justify-center">
|
|
217
|
+
<p className="text-lg font-medium">No agents yet</p>
|
|
218
|
+
<p className="text-sm">Create your first AI agent to get started.</p>
|
|
219
|
+
</div>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return (
|
|
224
|
+
<div className="space-y-4">
|
|
225
|
+
{/* Stats */}
|
|
226
|
+
{stats && (
|
|
227
|
+
<div className="flex gap-4 text-sm">
|
|
228
|
+
<span>Total: {stats.total}</span>
|
|
229
|
+
<span className="text-green-600">Active: {stats.active}</span>
|
|
230
|
+
<span className="text-yellow-600">Paused: {stats.paused}</span>
|
|
231
|
+
<span className="text-blue-600">Draft: {stats.draft}</span>
|
|
232
|
+
</div>
|
|
233
|
+
)}
|
|
234
|
+
|
|
235
|
+
{/* Agent Grid */}
|
|
236
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
237
|
+
{data.items.map((agent) => (
|
|
238
|
+
<AgentCard
|
|
239
|
+
key={agent.id}
|
|
240
|
+
agent={agent}
|
|
241
|
+
onClick={() => onAgentClick(agent)}
|
|
242
|
+
/>
|
|
243
|
+
))}
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Agent Card Component
|
|
251
|
+
*/
|
|
252
|
+
function AgentCard({ agent, onClick }: { agent: Agent; onClick: () => void }) {
|
|
253
|
+
const statusColors: Record<string, string> = {
|
|
254
|
+
ACTIVE:
|
|
255
|
+
'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400',
|
|
256
|
+
DRAFT: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400',
|
|
257
|
+
PAUSED:
|
|
258
|
+
'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400',
|
|
259
|
+
ARCHIVED: 'bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400',
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
return (
|
|
263
|
+
<div
|
|
264
|
+
onClick={onClick}
|
|
265
|
+
className="border-border bg-card cursor-pointer rounded-xl border p-4 transition-all hover:shadow-md"
|
|
266
|
+
role="button"
|
|
267
|
+
tabIndex={0}
|
|
268
|
+
onKeyDown={(e) => {
|
|
269
|
+
if (e.key === 'Enter' || e.key === ' ') onClick();
|
|
270
|
+
}}
|
|
271
|
+
>
|
|
272
|
+
<div className="flex items-start justify-between">
|
|
273
|
+
<div className="min-w-0 flex-1">
|
|
274
|
+
<h3 className="truncate font-semibold">{agent.name}</h3>
|
|
275
|
+
<p className="text-muted-foreground text-sm">
|
|
276
|
+
{agent.modelProvider} / {agent.modelName}
|
|
277
|
+
</p>
|
|
278
|
+
</div>
|
|
279
|
+
<span
|
|
280
|
+
className={`rounded-full px-2 py-0.5 text-xs font-medium ${statusColors[agent.status]}`}
|
|
281
|
+
>
|
|
282
|
+
{agent.status}
|
|
283
|
+
</span>
|
|
284
|
+
</div>
|
|
285
|
+
{agent.description && (
|
|
286
|
+
<p className="text-muted-foreground mt-2 line-clamp-2 text-sm">
|
|
287
|
+
{agent.description}
|
|
288
|
+
</p>
|
|
289
|
+
)}
|
|
290
|
+
<div className="mt-3 flex items-center justify-between">
|
|
291
|
+
<span className="text-muted-foreground text-xs">{agent.modelName}</span>
|
|
292
|
+
<Button variant="ghost" size="sm" onPress={onClick}>
|
|
293
|
+
Actions
|
|
294
|
+
</Button>
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Metrics View - Shows usage analytics
|
|
302
|
+
*/
|
|
303
|
+
function MetricsView({ metrics }: { metrics: RunMetrics | null }) {
|
|
304
|
+
if (!metrics) {
|
|
305
|
+
return (
|
|
306
|
+
<div className="text-muted-foreground flex h-64 items-center justify-center">
|
|
307
|
+
Loading metrics...
|
|
308
|
+
</div>
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Calculate derived metrics
|
|
313
|
+
const completedRuns = Math.round(metrics.totalRuns * metrics.successRate);
|
|
314
|
+
const failedRuns = metrics.totalRuns - completedRuns;
|
|
315
|
+
|
|
316
|
+
return (
|
|
317
|
+
<div className="space-y-6">
|
|
318
|
+
<h3 className="text-lg font-semibold">Usage Analytics</h3>
|
|
319
|
+
|
|
320
|
+
<div className="grid gap-6 md:grid-cols-2">
|
|
321
|
+
{/* Success/Failure breakdown */}
|
|
322
|
+
<div className="border-border bg-card rounded-xl border p-4">
|
|
323
|
+
<h4 className="font-medium">Run Outcomes</h4>
|
|
324
|
+
<div className="mt-4 space-y-3">
|
|
325
|
+
<ProgressBar
|
|
326
|
+
label="Completed"
|
|
327
|
+
value={completedRuns}
|
|
328
|
+
total={metrics.totalRuns}
|
|
329
|
+
color="bg-green-500"
|
|
330
|
+
/>
|
|
331
|
+
<ProgressBar
|
|
332
|
+
label="Failed"
|
|
333
|
+
value={failedRuns}
|
|
334
|
+
total={metrics.totalRuns}
|
|
335
|
+
color="bg-red-500"
|
|
336
|
+
/>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
{/* Key Stats */}
|
|
341
|
+
<div className="border-border bg-card rounded-xl border p-4">
|
|
342
|
+
<h4 className="font-medium">Performance</h4>
|
|
343
|
+
<dl className="mt-4 grid grid-cols-2 gap-4">
|
|
344
|
+
<div>
|
|
345
|
+
<dt className="text-muted-foreground text-sm">Avg Duration</dt>
|
|
346
|
+
<dd className="text-xl font-semibold">
|
|
347
|
+
{(metrics.averageDurationMs / 1000).toFixed(1)}s
|
|
348
|
+
</dd>
|
|
349
|
+
</div>
|
|
350
|
+
<div>
|
|
351
|
+
<dt className="text-muted-foreground text-sm">Success Rate</dt>
|
|
352
|
+
<dd className="text-xl font-semibold">
|
|
353
|
+
{(metrics.successRate * 100).toFixed(0)}%
|
|
354
|
+
</dd>
|
|
355
|
+
</div>
|
|
356
|
+
</dl>
|
|
357
|
+
</div>
|
|
358
|
+
</div>
|
|
359
|
+
|
|
360
|
+
{/* Key Metrics */}
|
|
361
|
+
<div className="border-border bg-card rounded-xl border p-4">
|
|
362
|
+
<h4 className="font-medium">Key Metrics</h4>
|
|
363
|
+
<dl className="mt-4 grid gap-4 sm:grid-cols-3">
|
|
364
|
+
<div>
|
|
365
|
+
<dt className="text-muted-foreground text-sm">Total Runs</dt>
|
|
366
|
+
<dd className="text-2xl font-semibold">
|
|
367
|
+
{metrics.totalRuns.toLocaleString()}
|
|
368
|
+
</dd>
|
|
369
|
+
</div>
|
|
370
|
+
<div>
|
|
371
|
+
<dt className="text-muted-foreground text-sm">Total Tokens</dt>
|
|
372
|
+
<dd className="text-2xl font-semibold">
|
|
373
|
+
{(metrics.totalTokens / 1000).toFixed(0)}K
|
|
374
|
+
</dd>
|
|
375
|
+
</div>
|
|
376
|
+
<div>
|
|
377
|
+
<dt className="text-muted-foreground text-sm">Cost per Run</dt>
|
|
378
|
+
<dd className="text-2xl font-semibold">
|
|
379
|
+
$
|
|
380
|
+
{metrics.totalRuns > 0
|
|
381
|
+
? (metrics.totalCostUsd / metrics.totalRuns).toFixed(4)
|
|
382
|
+
: '0'}
|
|
383
|
+
</dd>
|
|
384
|
+
</div>
|
|
385
|
+
</dl>
|
|
386
|
+
</div>
|
|
387
|
+
</div>
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function ProgressBar({
|
|
392
|
+
label,
|
|
393
|
+
value,
|
|
394
|
+
total,
|
|
395
|
+
color,
|
|
396
|
+
}: {
|
|
397
|
+
label: string;
|
|
398
|
+
value: number;
|
|
399
|
+
total: number;
|
|
400
|
+
color: string;
|
|
401
|
+
}) {
|
|
402
|
+
const pct = total > 0 ? (value / total) * 100 : 0;
|
|
403
|
+
return (
|
|
404
|
+
<div>
|
|
405
|
+
<div className="flex justify-between text-sm">
|
|
406
|
+
<span>{label}</span>
|
|
407
|
+
<span className="text-muted-foreground">
|
|
408
|
+
{value} ({pct.toFixed(0)}%)
|
|
409
|
+
</span>
|
|
410
|
+
</div>
|
|
411
|
+
<div className="bg-muted mt-1 h-2 overflow-hidden rounded-full">
|
|
412
|
+
<div className={`h-full ${color}`} style={{ width: `${pct}%` }} />
|
|
413
|
+
</div>
|
|
414
|
+
</div>
|
|
415
|
+
);
|
|
416
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Data hooks for agent-console template
|
|
5
|
+
*/
|
|
6
|
+
export { useAgentList, type UseAgentListOptions } from './useAgentList';
|
|
7
|
+
export { useRunList, type UseRunListOptions } from './useRunList';
|
|
8
|
+
export { useToolList, type UseToolListOptions } from './useToolList';
|
|
9
|
+
export {
|
|
10
|
+
useAgentMutations,
|
|
11
|
+
type UseAgentMutationsOptions,
|
|
12
|
+
type CreateAgentInput,
|
|
13
|
+
type UpdateAgentInput,
|
|
14
|
+
} from './useAgentMutations';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for fetching and managing agent list data
|
|
3
|
+
*
|
|
4
|
+
* Uses runtime-local database-backed handlers.
|
|
5
|
+
*/
|
|
6
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
7
|
+
import { useTemplateRuntime } from '@contractspec/lib.example-shared-ui';
|
|
8
|
+
import type {
|
|
9
|
+
Agent as RuntimeAgent,
|
|
10
|
+
ListAgentsOutput as RuntimeListAgentsOutput,
|
|
11
|
+
} from '../../handlers/agent.handlers';
|
|
12
|
+
import type { AgentHandlers } from '../../handlers/agent.handlers';
|
|
13
|
+
|
|
14
|
+
// Re-export types for convenience
|
|
15
|
+
export type Agent = RuntimeAgent;
|
|
16
|
+
export type ListAgentsOutput = RuntimeListAgentsOutput;
|
|
17
|
+
|
|
18
|
+
export interface UseAgentListOptions {
|
|
19
|
+
search?: string;
|
|
20
|
+
status?: 'DRAFT' | 'ACTIVE' | 'PAUSED' | 'ARCHIVED' | 'all';
|
|
21
|
+
limit?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function useAgentList(options: UseAgentListOptions = {}) {
|
|
25
|
+
const { handlers, projectId } = useTemplateRuntime<{
|
|
26
|
+
agent: AgentHandlers;
|
|
27
|
+
}>();
|
|
28
|
+
const { agent } = handlers;
|
|
29
|
+
|
|
30
|
+
const [data, setData] = useState<ListAgentsOutput | null>(null);
|
|
31
|
+
const [loading, setLoading] = useState(true);
|
|
32
|
+
const [error, setError] = useState<Error | null>(null);
|
|
33
|
+
const [page, setPage] = useState(1);
|
|
34
|
+
|
|
35
|
+
const fetchData = useCallback(async () => {
|
|
36
|
+
setLoading(true);
|
|
37
|
+
setError(null);
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const result = await agent.listAgents({
|
|
41
|
+
projectId,
|
|
42
|
+
search: options.search,
|
|
43
|
+
status: options.status === 'all' ? undefined : options.status,
|
|
44
|
+
limit: options.limit ?? 20,
|
|
45
|
+
offset: (page - 1) * (options.limit ?? 20),
|
|
46
|
+
});
|
|
47
|
+
setData(result);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
setError(err instanceof Error ? err : new Error('Unknown error'));
|
|
50
|
+
} finally {
|
|
51
|
+
setLoading(false);
|
|
52
|
+
}
|
|
53
|
+
}, [agent, projectId, options.search, options.status, options.limit, page]);
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
fetchData();
|
|
57
|
+
}, [fetchData]);
|
|
58
|
+
|
|
59
|
+
// Calculate stats
|
|
60
|
+
const stats = useMemo(() => {
|
|
61
|
+
if (!data) return null;
|
|
62
|
+
return {
|
|
63
|
+
total: data.total,
|
|
64
|
+
active: data.items.filter((a) => a.status === 'ACTIVE').length,
|
|
65
|
+
paused: data.items.filter((a) => a.status === 'PAUSED').length,
|
|
66
|
+
draft: data.items.filter((a) => a.status === 'DRAFT').length,
|
|
67
|
+
};
|
|
68
|
+
}, [data]);
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
data,
|
|
72
|
+
loading,
|
|
73
|
+
error,
|
|
74
|
+
stats,
|
|
75
|
+
page,
|
|
76
|
+
refetch: fetchData,
|
|
77
|
+
nextPage: () => setPage((p) => p + 1),
|
|
78
|
+
prevPage: () => page > 1 && setPage((p) => p - 1),
|
|
79
|
+
};
|
|
80
|
+
}
|