plugin-agent-orchestrator 1.0.22 → 1.0.23
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/client-v2.d.ts +2 -0
- package/client-v2.js +1 -0
- package/dist/client/index.js +1 -1
- package/dist/client-v2/214.723affb37c13bf7a.js +10 -0
- package/dist/client-v2/264.0533912e6c5ea2d7.js +10 -0
- package/dist/client-v2/41.1805b2edfaa4afe2.js +10 -0
- package/dist/client-v2/418.5ae055abf141820e.js +10 -0
- package/dist/client-v2/619.d99d3c9e61c99064.js +10 -0
- package/dist/client-v2/70.a15d7fcec7c41768.js +10 -0
- package/dist/client-v2/892.72db4161511c8a16.js +10 -0
- package/dist/client-v2/926.87f660b670d85bcc.js +10 -0
- package/dist/client-v2/index.js +10 -0
- package/dist/externalVersion.js +7 -6
- package/dist/locale/en-US.json +7 -0
- package/dist/locale/vi-VN.json +7 -0
- package/dist/locale/zh-CN.json +27 -0
- package/dist/server/migrations/20260615000000-normalize-ai-employee-tool-bindings.js +63 -0
- package/dist/server/plugin.js +32 -1
- package/dist/server/services/AgentHarness.js +52 -27
- package/dist/server/services/AgentLoopController.js +8 -2
- package/dist/server/services/AgentLoopService.js +1 -1
- package/dist/server/services/AgentRegistryService.js +53 -42
- package/dist/server/services/CircuitBreaker.js +7 -2
- package/dist/server/services/CodeValidator.js +48 -14
- package/dist/server/services/SandboxRunner.js +18 -14
- package/dist/server/skill-hub/plugin.js +44 -17
- package/dist/server/tools/delegate-task.js +7 -2
- package/dist/server/tools/skill-execute.js +33 -2
- package/dist/server/utils/ai-manager.js +51 -0
- package/dist/server/utils/ctx-utils.js +11 -0
- package/dist/server/utils/skill-settings.js +122 -0
- package/package.json +49 -45
- package/src/client/AIEmployeesContext.tsx +51 -14
- package/src/client/AgentRunsTab.tsx +767 -764
- package/src/client/HarnessProfilesTab.tsx +254 -247
- package/src/client/RulesTab.tsx +780 -716
- package/src/client/TracingTab.tsx +1 -0
- package/src/client/plugin.tsx +34 -27
- package/src/client/skill-hub/components/GitSkillImport.tsx +10 -3
- package/src/client/skill-hub/components/SkillMetrics.tsx +157 -124
- package/src/client/skill-hub/index.tsx +58 -51
- package/src/client/skill-hub/tools/InteractionSchemasProvider.tsx +132 -99
- package/src/client/skill-hub/tools/registerSkillLoopCards.ts +71 -58
- package/src/client/tools/registerOrchestratorCards.ts +17 -7
- package/src/client-v2/components/AIEmployeeSelect.tsx +47 -0
- package/src/client-v2/components/AIEmployeesContext.tsx +110 -0
- package/src/client-v2/components/AgentRunsTab.tsx +767 -0
- package/src/client-v2/components/HarnessProfilesTab.tsx +254 -0
- package/src/client-v2/components/RulesTab.tsx +782 -0
- package/src/client-v2/components/TracingTab.tsx +432 -0
- package/src/client-v2/hooks/useApiRequest.ts +114 -0
- package/src/client-v2/index.tsx +1 -0
- package/src/client-v2/pages/AgentRunsPage.tsx +13 -0
- package/src/client-v2/pages/ExecutionHistoryPage.tsx +10 -0
- package/src/client-v2/pages/HarnessProfilesPage.tsx +10 -0
- package/src/client-v2/pages/LoopSettingsPage.tsx +10 -0
- package/src/client-v2/pages/RulesPage.tsx +13 -0
- package/src/client-v2/pages/SkillDefinitionsPage.tsx +10 -0
- package/src/client-v2/pages/SkillMetricsPage.tsx +10 -0
- package/src/client-v2/pages/TracingPage.tsx +13 -0
- package/src/client-v2/plugin.tsx +70 -0
- package/src/client-v2/skill-hub/components/ExecutionHistory.tsx +196 -0
- package/src/client-v2/skill-hub/components/FileLinkList.tsx +37 -0
- package/src/client-v2/skill-hub/components/GitSkillImport.tsx +539 -0
- package/src/client-v2/skill-hub/components/LoopSettings.tsx +331 -0
- package/src/client-v2/skill-hub/components/SkillEditor.tsx +453 -0
- package/src/client-v2/skill-hub/components/SkillManager.tsx +174 -0
- package/src/client-v2/skill-hub/components/SkillMetrics.tsx +157 -0
- package/src/client-v2/skill-hub/components/SkillTestPanel.tsx +135 -0
- package/src/client-v2/skill-hub/locale.ts +13 -0
- package/src/client-v2/skill-hub/tools/loopTemplates.ts +52 -0
- package/src/client-v2/skill-hub/utils/jsonFields.ts +41 -0
- package/src/client-v2/utils/jsonFields.ts +41 -0
- package/src/locale/en-US.json +7 -0
- package/src/locale/vi-VN.json +7 -0
- package/src/locale/zh-CN.json +27 -0
- package/src/server/__tests__/agent-registry-service.test.ts +147 -0
- package/src/server/__tests__/code-validator.test.ts +63 -0
- package/src/server/__tests__/skill-execute.test.ts +33 -0
- package/src/server/__tests__/skill-settings.test.ts +63 -0
- package/src/server/migrations/20260615000000-normalize-ai-employee-tool-bindings.ts +39 -0
- package/src/server/plugin.ts +62 -21
- package/src/server/services/AgentHarness.ts +49 -22
- package/src/server/services/AgentLoopController.ts +17 -6
- package/src/server/services/AgentLoopService.ts +1 -1
- package/src/server/services/AgentPlannerService.ts +10 -0
- package/src/server/services/AgentRegistryService.ts +89 -47
- package/src/server/services/CircuitBreaker.ts +10 -0
- package/src/server/services/CodeValidator.ts +237 -159
- package/src/server/services/SandboxRunner.ts +203 -189
- package/src/server/skill-hub/plugin.ts +933 -898
- package/src/server/tools/delegate-task.ts +12 -9
- package/src/server/tools/skill-execute.ts +194 -160
- package/src/server/utils/ai-manager.ts +24 -0
- package/src/server/utils/ctx-utils.ts +14 -0
- package/src/server/utils/skill-settings.ts +116 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import React, { useMemo, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Table,
|
|
4
|
+
Card,
|
|
5
|
+
Tag,
|
|
6
|
+
Typography,
|
|
7
|
+
Drawer,
|
|
8
|
+
Descriptions,
|
|
9
|
+
Alert,
|
|
10
|
+
Button,
|
|
11
|
+
Empty,
|
|
12
|
+
Space,
|
|
13
|
+
Timeline,
|
|
14
|
+
Collapse,
|
|
15
|
+
Spin,
|
|
16
|
+
Select,
|
|
17
|
+
DatePicker,
|
|
18
|
+
Form,
|
|
19
|
+
} from 'antd';
|
|
20
|
+
import { EyeOutlined, CheckCircleOutlined, CloseCircleOutlined, ReloadOutlined } from '@ant-design/icons';
|
|
21
|
+
import { useApiClient as useAPIClient, useRequest } from '../hooks/useApiRequest';
|
|
22
|
+
import { useAIEmployees } from './AIEmployeesContext';
|
|
23
|
+
|
|
24
|
+
const { Text, Paragraph } = Typography;
|
|
25
|
+
const { RangePicker } = DatePicker;
|
|
26
|
+
|
|
27
|
+
type FilterState = {
|
|
28
|
+
leader?: string;
|
|
29
|
+
subAgent?: string;
|
|
30
|
+
status?: string;
|
|
31
|
+
range?: [any, any] | null;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const TracingTab: React.FC = () => {
|
|
35
|
+
const api = useAPIClient();
|
|
36
|
+
const [selectedLog, setSelectedLog] = useState<any>(null);
|
|
37
|
+
const [detailLoading, setDetailLoading] = useState(false);
|
|
38
|
+
|
|
39
|
+
const [page, setPage] = useState(1);
|
|
40
|
+
const [pageSize, setPageSize] = useState(20);
|
|
41
|
+
const [filters, setFilters] = useState<FilterState>({});
|
|
42
|
+
|
|
43
|
+
const { employees, employeeMap } = useAIEmployees();
|
|
44
|
+
|
|
45
|
+
const requestParams = useMemo(() => {
|
|
46
|
+
const filter: any = {};
|
|
47
|
+
if (filters.leader) filter.leaderUsername = filters.leader;
|
|
48
|
+
if (filters.subAgent) filter.subAgentUsername = filters.subAgent;
|
|
49
|
+
if (filters.status) filter.status = filters.status;
|
|
50
|
+
if (filters.range && (filters.range[0] || filters.range[1])) {
|
|
51
|
+
filter.createdAt = {};
|
|
52
|
+
if (filters.range[0]) filter.createdAt.$gte = filters.range[0].toDate().toISOString();
|
|
53
|
+
if (filters.range[1]) filter.createdAt.$lte = filters.range[1].toDate().toISOString();
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
sort: ['-createdAt'],
|
|
57
|
+
page,
|
|
58
|
+
pageSize,
|
|
59
|
+
filter,
|
|
60
|
+
};
|
|
61
|
+
}, [page, pageSize, filters]);
|
|
62
|
+
|
|
63
|
+
const { data, loading, refresh } = useRequest(
|
|
64
|
+
{
|
|
65
|
+
url: 'orchestratorTracing:list',
|
|
66
|
+
params: requestParams,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
refreshDeps: [requestParams],
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Server returns { data: rows, meta: { count } }; useRequest unwraps to that shape.
|
|
74
|
+
const logs = useMemo(() => {
|
|
75
|
+
const rows = (data as any)?.data;
|
|
76
|
+
return Array.isArray(rows) ? rows : [];
|
|
77
|
+
}, [data]);
|
|
78
|
+
|
|
79
|
+
const total = useMemo(() => {
|
|
80
|
+
const count = (data as any)?.meta?.count;
|
|
81
|
+
return typeof count === 'number' ? count : 0;
|
|
82
|
+
}, [data]);
|
|
83
|
+
|
|
84
|
+
const formatDuration = (ms: number) => {
|
|
85
|
+
if (!ms) return '-';
|
|
86
|
+
return ms >= 1000 ? `${(ms / 1000).toFixed(1)}s` : `${ms}ms`;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const handleOpenLog = async (record: any) => {
|
|
90
|
+
setSelectedLog(record);
|
|
91
|
+
setDetailLoading(true);
|
|
92
|
+
try {
|
|
93
|
+
const res = await api.request({
|
|
94
|
+
url: 'orchestratorTracing:get',
|
|
95
|
+
params: { filterByTk: record.id, source: record.hasUnifiedTrace ? 'span' : 'log' },
|
|
96
|
+
});
|
|
97
|
+
setSelectedLog((res as any)?.data?.data || (res as any)?.data || record);
|
|
98
|
+
} finally {
|
|
99
|
+
setDetailLoading(false);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const updateFilter = (patch: Partial<FilterState>) => {
|
|
104
|
+
setFilters((prev) => ({ ...prev, ...patch }));
|
|
105
|
+
setPage(1);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const resetFilters = () => {
|
|
109
|
+
setFilters({});
|
|
110
|
+
setPage(1);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const employeeOptions = useMemo(
|
|
114
|
+
() =>
|
|
115
|
+
employees.map((e) => ({
|
|
116
|
+
label: e.nickname || e.username,
|
|
117
|
+
value: e.username,
|
|
118
|
+
})),
|
|
119
|
+
[employees],
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const hasFilters = Boolean(
|
|
123
|
+
filters.leader || filters.subAgent || filters.status || (filters.range && (filters.range[0] || filters.range[1])),
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const columns = [
|
|
127
|
+
{
|
|
128
|
+
title: 'Time',
|
|
129
|
+
dataIndex: 'createdAt',
|
|
130
|
+
key: 'createdAt',
|
|
131
|
+
width: 170,
|
|
132
|
+
render: (v: string) => (v ? new Date(v).toLocaleString() : '-'),
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
title: 'Leader',
|
|
136
|
+
dataIndex: 'leaderUsername',
|
|
137
|
+
key: 'leaderUsername',
|
|
138
|
+
render: (username: string) => <Tag color="blue">{employeeMap.get(username) || username}</Tag>,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
title: 'Sub-Agent',
|
|
142
|
+
dataIndex: 'subAgentUsername',
|
|
143
|
+
key: 'subAgentUsername',
|
|
144
|
+
render: (username: string) => <Tag color="green">{employeeMap.get(username) || username}</Tag>,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
title: 'Task',
|
|
148
|
+
dataIndex: 'task',
|
|
149
|
+
key: 'task',
|
|
150
|
+
render: (task: string) => (
|
|
151
|
+
<Text ellipsis style={{ maxWidth: 280 }}>
|
|
152
|
+
{task?.substring(0, 100) || '-'}
|
|
153
|
+
</Text>
|
|
154
|
+
),
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
title: 'Status',
|
|
158
|
+
dataIndex: 'status',
|
|
159
|
+
key: 'status',
|
|
160
|
+
width: 90,
|
|
161
|
+
render: (status: string) => (
|
|
162
|
+
<Tag
|
|
163
|
+
icon={status === 'success' ? <CheckCircleOutlined /> : <CloseCircleOutlined />}
|
|
164
|
+
color={status === 'success' ? 'success' : 'error'}
|
|
165
|
+
>
|
|
166
|
+
{status}
|
|
167
|
+
</Tag>
|
|
168
|
+
),
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
title: 'Duration',
|
|
172
|
+
dataIndex: 'durationMs',
|
|
173
|
+
key: 'durationMs',
|
|
174
|
+
width: 90,
|
|
175
|
+
render: formatDuration,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
title: 'Depth',
|
|
179
|
+
dataIndex: 'depth',
|
|
180
|
+
key: 'depth',
|
|
181
|
+
width: 60,
|
|
182
|
+
render: (v: number) => v ?? 0,
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
title: '',
|
|
186
|
+
key: 'actions',
|
|
187
|
+
width: 80,
|
|
188
|
+
render: (_: any, record: any) => (
|
|
189
|
+
<Button type="link" size="small" icon={<EyeOutlined />} onClick={() => handleOpenLog(record)}>
|
|
190
|
+
Detail
|
|
191
|
+
</Button>
|
|
192
|
+
),
|
|
193
|
+
},
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<div>
|
|
198
|
+
<Alert
|
|
199
|
+
type="info"
|
|
200
|
+
showIcon
|
|
201
|
+
style={{ marginBottom: 16 }}
|
|
202
|
+
message="Execution Tracing"
|
|
203
|
+
description={
|
|
204
|
+
<Text type="secondary">
|
|
205
|
+
View orchestration execution logs. Each row represents one sub-agent invocation, with child tool and Skill
|
|
206
|
+
Hub executions shown in the detail flow.
|
|
207
|
+
</Text>
|
|
208
|
+
}
|
|
209
|
+
/>
|
|
210
|
+
|
|
211
|
+
<Card bordered={false}>
|
|
212
|
+
<Form layout="inline" style={{ marginBottom: 16, rowGap: 8, flexWrap: 'wrap' }}>
|
|
213
|
+
<Form.Item label="Leader">
|
|
214
|
+
<Select
|
|
215
|
+
allowClear
|
|
216
|
+
placeholder="Any leader"
|
|
217
|
+
style={{ minWidth: 180 }}
|
|
218
|
+
options={employeeOptions}
|
|
219
|
+
value={filters.leader}
|
|
220
|
+
onChange={(v) => updateFilter({ leader: v })}
|
|
221
|
+
showSearch
|
|
222
|
+
optionFilterProp="label"
|
|
223
|
+
/>
|
|
224
|
+
</Form.Item>
|
|
225
|
+
<Form.Item label="Sub-Agent">
|
|
226
|
+
<Select
|
|
227
|
+
allowClear
|
|
228
|
+
placeholder="Any sub-agent"
|
|
229
|
+
style={{ minWidth: 180 }}
|
|
230
|
+
options={employeeOptions}
|
|
231
|
+
value={filters.subAgent}
|
|
232
|
+
onChange={(v) => updateFilter({ subAgent: v })}
|
|
233
|
+
showSearch
|
|
234
|
+
optionFilterProp="label"
|
|
235
|
+
/>
|
|
236
|
+
</Form.Item>
|
|
237
|
+
<Form.Item label="Status">
|
|
238
|
+
<Select
|
|
239
|
+
allowClear
|
|
240
|
+
placeholder="Any status"
|
|
241
|
+
style={{ minWidth: 140 }}
|
|
242
|
+
options={[
|
|
243
|
+
{ label: 'Success', value: 'success' },
|
|
244
|
+
{ label: 'Error', value: 'error' },
|
|
245
|
+
{ label: 'Running', value: 'running' },
|
|
246
|
+
]}
|
|
247
|
+
value={filters.status}
|
|
248
|
+
onChange={(v) => updateFilter({ status: v })}
|
|
249
|
+
/>
|
|
250
|
+
</Form.Item>
|
|
251
|
+
<Form.Item label="Time">
|
|
252
|
+
<RangePicker showTime value={filters.range as any} onChange={(v) => updateFilter({ range: v as any })} />
|
|
253
|
+
</Form.Item>
|
|
254
|
+
<Form.Item>
|
|
255
|
+
<Space>
|
|
256
|
+
<Button onClick={resetFilters} disabled={!hasFilters}>
|
|
257
|
+
Reset
|
|
258
|
+
</Button>
|
|
259
|
+
<Button icon={<ReloadOutlined />} onClick={refresh}>
|
|
260
|
+
Refresh
|
|
261
|
+
</Button>
|
|
262
|
+
</Space>
|
|
263
|
+
</Form.Item>
|
|
264
|
+
</Form>
|
|
265
|
+
|
|
266
|
+
<Table
|
|
267
|
+
rowKey="id"
|
|
268
|
+
loading={loading}
|
|
269
|
+
dataSource={logs}
|
|
270
|
+
columns={columns}
|
|
271
|
+
size="middle"
|
|
272
|
+
scroll={{ x: 'max-content' }}
|
|
273
|
+
pagination={{
|
|
274
|
+
current: page,
|
|
275
|
+
pageSize,
|
|
276
|
+
total,
|
|
277
|
+
showSizeChanger: true,
|
|
278
|
+
pageSizeOptions: [10, 20, 50, 100],
|
|
279
|
+
showTotal: (count) => `${count} execution${count === 1 ? '' : 's'}`,
|
|
280
|
+
onChange: (nextPage, nextSize) => {
|
|
281
|
+
setPage(nextPage);
|
|
282
|
+
if (nextSize && nextSize !== pageSize) setPageSize(nextSize);
|
|
283
|
+
},
|
|
284
|
+
}}
|
|
285
|
+
locale={{
|
|
286
|
+
emptyText: (
|
|
287
|
+
<Empty
|
|
288
|
+
description={hasFilters ? 'No executions match the current filters' : 'No delegation executions yet'}
|
|
289
|
+
/>
|
|
290
|
+
),
|
|
291
|
+
}}
|
|
292
|
+
/>
|
|
293
|
+
</Card>
|
|
294
|
+
|
|
295
|
+
<Drawer title="Execution Detail" width={820} onClose={() => setSelectedLog(null)} open={!!selectedLog}>
|
|
296
|
+
{selectedLog && (
|
|
297
|
+
<Spin spinning={detailLoading}>
|
|
298
|
+
<>
|
|
299
|
+
<Descriptions column={1} bordered size="small" style={{ marginBottom: 16 }}>
|
|
300
|
+
<Descriptions.Item label="Status">
|
|
301
|
+
<Tag
|
|
302
|
+
icon={selectedLog.status === 'success' ? <CheckCircleOutlined /> : <CloseCircleOutlined />}
|
|
303
|
+
color={selectedLog.status === 'success' ? 'success' : 'error'}
|
|
304
|
+
>
|
|
305
|
+
{selectedLog.status}
|
|
306
|
+
</Tag>
|
|
307
|
+
</Descriptions.Item>
|
|
308
|
+
<Descriptions.Item label="Leader">
|
|
309
|
+
<Tag color="blue">{employeeMap.get(selectedLog.leaderUsername) || selectedLog.leaderUsername}</Tag>
|
|
310
|
+
</Descriptions.Item>
|
|
311
|
+
<Descriptions.Item label="Sub-Agent">
|
|
312
|
+
<Tag color="green">
|
|
313
|
+
{employeeMap.get(selectedLog.subAgentUsername) || selectedLog.subAgentUsername}
|
|
314
|
+
</Tag>
|
|
315
|
+
</Descriptions.Item>
|
|
316
|
+
<Descriptions.Item label="Tool">
|
|
317
|
+
<Text code>{selectedLog.toolName}</Text>
|
|
318
|
+
</Descriptions.Item>
|
|
319
|
+
<Descriptions.Item label="Depth">{selectedLog.depth ?? 0}</Descriptions.Item>
|
|
320
|
+
<Descriptions.Item label="Duration">{formatDuration(selectedLog.durationMs)}</Descriptions.Item>
|
|
321
|
+
<Descriptions.Item label="Time">
|
|
322
|
+
{selectedLog.createdAt ? new Date(selectedLog.createdAt).toLocaleString() : '-'}
|
|
323
|
+
</Descriptions.Item>
|
|
324
|
+
</Descriptions>
|
|
325
|
+
|
|
326
|
+
<Card title="Task" size="small" style={{ marginBottom: 16 }}>
|
|
327
|
+
<Paragraph style={{ whiteSpace: 'pre-wrap', margin: 0, fontSize: 13 }}>
|
|
328
|
+
{selectedLog.task || 'No task description'}
|
|
329
|
+
</Paragraph>
|
|
330
|
+
</Card>
|
|
331
|
+
|
|
332
|
+
{selectedLog.context && (
|
|
333
|
+
<Card title="Context" size="small" style={{ marginBottom: 16 }}>
|
|
334
|
+
<Paragraph style={{ whiteSpace: 'pre-wrap', margin: 0, fontSize: 13 }}>
|
|
335
|
+
{selectedLog.context}
|
|
336
|
+
</Paragraph>
|
|
337
|
+
</Card>
|
|
338
|
+
)}
|
|
339
|
+
|
|
340
|
+
<Card title="Execution Flow" size="small" style={{ marginBottom: 16 }}>
|
|
341
|
+
{Array.isArray(selectedLog.trace) && selectedLog.trace.length ? (
|
|
342
|
+
<Timeline
|
|
343
|
+
items={selectedLog.trace.map((item: any, index: number) => ({
|
|
344
|
+
key: index,
|
|
345
|
+
color: item.status === 'error' ? 'red' : item.type === 'tool_call' ? 'blue' : 'green',
|
|
346
|
+
children: (
|
|
347
|
+
<div>
|
|
348
|
+
<Space direction="vertical" size={2} style={{ width: '100%' }}>
|
|
349
|
+
<Text strong>{item.title || item.type}</Text>
|
|
350
|
+
<Text type="secondary">{item.at ? new Date(item.at).toLocaleString() : ''}</Text>
|
|
351
|
+
{item.toolName && <Text code>{item.toolName}</Text>}
|
|
352
|
+
{item.skillExecutionId && (
|
|
353
|
+
<Text type="secondary">Skill execution #{item.skillExecutionId}</Text>
|
|
354
|
+
)}
|
|
355
|
+
{item.content && (
|
|
356
|
+
<Paragraph style={{ whiteSpace: 'pre-wrap', margin: 0, fontSize: 13 }}>
|
|
357
|
+
{item.content}
|
|
358
|
+
</Paragraph>
|
|
359
|
+
)}
|
|
360
|
+
{item.args && (
|
|
361
|
+
<Paragraph style={{ whiteSpace: 'pre-wrap', margin: 0, fontSize: 12 }}>
|
|
362
|
+
{JSON.stringify(item.args, null, 2)}
|
|
363
|
+
</Paragraph>
|
|
364
|
+
)}
|
|
365
|
+
</Space>
|
|
366
|
+
</div>
|
|
367
|
+
),
|
|
368
|
+
}))}
|
|
369
|
+
/>
|
|
370
|
+
) : (
|
|
371
|
+
<Empty description="No execution flow captured" />
|
|
372
|
+
)}
|
|
373
|
+
</Card>
|
|
374
|
+
|
|
375
|
+
{Array.isArray(selectedLog.messages) && selectedLog.messages.length > 0 && (
|
|
376
|
+
<Collapse
|
|
377
|
+
style={{ marginBottom: 16 }}
|
|
378
|
+
items={[
|
|
379
|
+
{
|
|
380
|
+
key: 'messages',
|
|
381
|
+
label: `Raw messages (${selectedLog.messages.length})`,
|
|
382
|
+
children: (
|
|
383
|
+
<Space direction="vertical" style={{ width: '100%' }}>
|
|
384
|
+
{selectedLog.messages.map((message: any) => (
|
|
385
|
+
<Card key={message.index} size="small" title={`${message.index + 1}. ${message.type}`}>
|
|
386
|
+
<Paragraph style={{ whiteSpace: 'pre-wrap', margin: 0, fontSize: 12 }}>
|
|
387
|
+
{message.content || JSON.stringify(message.toolCalls || message, null, 2)}
|
|
388
|
+
</Paragraph>
|
|
389
|
+
{message.toolCalls?.length > 0 && (
|
|
390
|
+
<Paragraph style={{ whiteSpace: 'pre-wrap', margin: '8px 0 0', fontSize: 12 }}>
|
|
391
|
+
{JSON.stringify(message.toolCalls, null, 2)}
|
|
392
|
+
</Paragraph>
|
|
393
|
+
)}
|
|
394
|
+
</Card>
|
|
395
|
+
))}
|
|
396
|
+
</Space>
|
|
397
|
+
),
|
|
398
|
+
},
|
|
399
|
+
]}
|
|
400
|
+
/>
|
|
401
|
+
)}
|
|
402
|
+
|
|
403
|
+
<Card
|
|
404
|
+
title="Result"
|
|
405
|
+
size="small"
|
|
406
|
+
style={{
|
|
407
|
+
marginBottom: 16,
|
|
408
|
+
borderColor: selectedLog.status === 'success' ? '#b7eb8f' : '#ffa39e',
|
|
409
|
+
}}
|
|
410
|
+
>
|
|
411
|
+
<Paragraph
|
|
412
|
+
style={{ whiteSpace: 'pre-wrap', margin: 0, fontSize: 13 }}
|
|
413
|
+
ellipsis={{ rows: 20, expandable: true }}
|
|
414
|
+
>
|
|
415
|
+
{selectedLog.result || selectedLog.error || 'No result'}
|
|
416
|
+
</Paragraph>
|
|
417
|
+
</Card>
|
|
418
|
+
|
|
419
|
+
{selectedLog.error && (
|
|
420
|
+
<Card title="Error" size="small" style={{ borderColor: '#ffa39e' }}>
|
|
421
|
+
<Paragraph type="danger" style={{ whiteSpace: 'pre-wrap', margin: 0, fontSize: 13 }}>
|
|
422
|
+
{selectedLog.error}
|
|
423
|
+
</Paragraph>
|
|
424
|
+
</Card>
|
|
425
|
+
)}
|
|
426
|
+
</>
|
|
427
|
+
</Spin>
|
|
428
|
+
)}
|
|
429
|
+
</Drawer>
|
|
430
|
+
</div>
|
|
431
|
+
);
|
|
432
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { useApp } from '@nocobase/client-v2';
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* client-v2 adapters for the two data-layer hooks the v1 admin tabs relied on
|
|
6
|
+
* (`useAPIClient` and `useRequest` from `@nocobase/client`). client-v2 exposes
|
|
7
|
+
* neither, so these reimplement just the slice the orchestrator UI uses on top
|
|
8
|
+
* of `useApp().apiClient`, keeping the call sites unchanged.
|
|
9
|
+
*
|
|
10
|
+
* v1 contract preserved here:
|
|
11
|
+
* - `useRequest` resolves `data` to the response BODY (`response.data`), so a
|
|
12
|
+
* list endpoint returning `{ data: rows, meta }` is read as `result.data.data`
|
|
13
|
+
* / `result.data.meta` — identical to the v1 tabs.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export type ApiRequestService =
|
|
17
|
+
| {
|
|
18
|
+
url: string;
|
|
19
|
+
method?: string;
|
|
20
|
+
params?: Record<string, unknown>;
|
|
21
|
+
data?: unknown;
|
|
22
|
+
}
|
|
23
|
+
| (() => Promise<unknown>);
|
|
24
|
+
|
|
25
|
+
export type ApiRequestOptions = {
|
|
26
|
+
/** Do not fire on mount; caller triggers via `run`. */
|
|
27
|
+
manual?: boolean;
|
|
28
|
+
/** Re-run whenever any dependency changes (mirrors ahooks refreshDeps). */
|
|
29
|
+
refreshDeps?: unknown[];
|
|
30
|
+
onSuccess?: (data: unknown) => void;
|
|
31
|
+
onError?: (error: unknown) => void;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type ApiRequestResult<TData = unknown> = {
|
|
35
|
+
data: TData | undefined;
|
|
36
|
+
loading: boolean;
|
|
37
|
+
error: unknown;
|
|
38
|
+
refresh: () => void;
|
|
39
|
+
run: (overrideParams?: Record<string, unknown>) => Promise<TData | undefined>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** Returns the v2 application's API client (replacement for v1 `useAPIClient`). */
|
|
43
|
+
export function useApiClient() {
|
|
44
|
+
return useApp().apiClient;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Minimal `useRequest` replacement. Supports object services
|
|
49
|
+
* (`{ url, method, params }`) and function services, plus `manual`,
|
|
50
|
+
* `refreshDeps`, and `onSuccess`/`onError` — the only options the tabs use.
|
|
51
|
+
*/
|
|
52
|
+
export function useRequest<TData = unknown>(
|
|
53
|
+
service: ApiRequestService,
|
|
54
|
+
options: ApiRequestOptions = {},
|
|
55
|
+
): ApiRequestResult<TData> {
|
|
56
|
+
const api = useApiClient();
|
|
57
|
+
const { manual = false, refreshDeps = [], onSuccess, onError } = options;
|
|
58
|
+
|
|
59
|
+
const [data, setData] = useState<TData | undefined>(undefined);
|
|
60
|
+
const [loading, setLoading] = useState<boolean>(!manual);
|
|
61
|
+
const [error, setError] = useState<unknown>(undefined);
|
|
62
|
+
|
|
63
|
+
// Keep the latest service/callbacks without forcing them into refreshDeps.
|
|
64
|
+
const serviceRef = useRef(service);
|
|
65
|
+
serviceRef.current = service;
|
|
66
|
+
const onSuccessRef = useRef(onSuccess);
|
|
67
|
+
onSuccessRef.current = onSuccess;
|
|
68
|
+
const onErrorRef = useRef(onError);
|
|
69
|
+
onErrorRef.current = onError;
|
|
70
|
+
|
|
71
|
+
const run = useCallback(
|
|
72
|
+
async (overrideParams?: Record<string, unknown>): Promise<TData | undefined> => {
|
|
73
|
+
const current = serviceRef.current;
|
|
74
|
+
setLoading(true);
|
|
75
|
+
setError(undefined);
|
|
76
|
+
try {
|
|
77
|
+
let body: TData;
|
|
78
|
+
if (typeof current === 'function') {
|
|
79
|
+
body = (await current()) as TData;
|
|
80
|
+
} else {
|
|
81
|
+
const response = await api.request({
|
|
82
|
+
url: current.url,
|
|
83
|
+
method: current.method || 'get',
|
|
84
|
+
params: { ...(current.params || {}), ...(overrideParams || {}) },
|
|
85
|
+
data: current.data,
|
|
86
|
+
});
|
|
87
|
+
body = response?.data as TData;
|
|
88
|
+
}
|
|
89
|
+
setData(body);
|
|
90
|
+
onSuccessRef.current?.(body);
|
|
91
|
+
return body;
|
|
92
|
+
} catch (err) {
|
|
93
|
+
setError(err);
|
|
94
|
+
onErrorRef.current?.(err);
|
|
95
|
+
return undefined;
|
|
96
|
+
} finally {
|
|
97
|
+
setLoading(false);
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
[api],
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const refresh = useCallback(() => {
|
|
104
|
+
run();
|
|
105
|
+
}, [run]);
|
|
106
|
+
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (manual) return;
|
|
109
|
+
run();
|
|
110
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
111
|
+
}, refreshDeps);
|
|
112
|
+
|
|
113
|
+
return { data, loading, error, refresh, run };
|
|
114
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './plugin';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AIEmployeesProvider } from '../components/AIEmployeesContext';
|
|
3
|
+
import { AgentRunsTab } from '../components/AgentRunsTab';
|
|
4
|
+
|
|
5
|
+
const AgentRunsPage: React.FC = () => (
|
|
6
|
+
<AIEmployeesProvider>
|
|
7
|
+
<div style={{ padding: '0 24px 24px' }}>
|
|
8
|
+
<AgentRunsTab />
|
|
9
|
+
</div>
|
|
10
|
+
</AIEmployeesProvider>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export default AgentRunsPage;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ExecutionHistory } from '../skill-hub/components/ExecutionHistory';
|
|
3
|
+
|
|
4
|
+
const ExecutionHistoryPage: React.FC = () => (
|
|
5
|
+
<div style={{ padding: '0 24px 24px' }}>
|
|
6
|
+
<ExecutionHistory />
|
|
7
|
+
</div>
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
export default ExecutionHistoryPage;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { HarnessProfilesTab } from '../components/HarnessProfilesTab';
|
|
3
|
+
|
|
4
|
+
const HarnessProfilesPage: React.FC = () => (
|
|
5
|
+
<div style={{ padding: '0 24px 24px' }}>
|
|
6
|
+
<HarnessProfilesTab />
|
|
7
|
+
</div>
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
export default HarnessProfilesPage;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AIEmployeesProvider } from '../components/AIEmployeesContext';
|
|
3
|
+
import { RulesTab } from '../components/RulesTab';
|
|
4
|
+
|
|
5
|
+
const RulesPage: React.FC = () => (
|
|
6
|
+
<AIEmployeesProvider>
|
|
7
|
+
<div style={{ padding: '0 24px 24px' }}>
|
|
8
|
+
<RulesTab />
|
|
9
|
+
</div>
|
|
10
|
+
</AIEmployeesProvider>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export default RulesPage;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { SkillManager } from '../skill-hub/components/SkillManager';
|
|
3
|
+
|
|
4
|
+
const SkillDefinitionsPage: React.FC = () => (
|
|
5
|
+
<div style={{ padding: '0 24px 24px' }}>
|
|
6
|
+
<SkillManager />
|
|
7
|
+
</div>
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
export default SkillDefinitionsPage;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AIEmployeesProvider } from '../components/AIEmployeesContext';
|
|
3
|
+
import { TracingTab } from '../components/TracingTab';
|
|
4
|
+
|
|
5
|
+
const TracingPage: React.FC = () => (
|
|
6
|
+
<AIEmployeesProvider>
|
|
7
|
+
<div style={{ padding: '0 24px 24px' }}>
|
|
8
|
+
<TracingTab />
|
|
9
|
+
</div>
|
|
10
|
+
</AIEmployeesProvider>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export default TracingPage;
|