plugin-agent-orchestrator 1.0.28 → 1.0.32

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.
Files changed (108) hide show
  1. package/README.md +9 -7
  2. package/dist/client/index.js +1 -1
  3. package/dist/client-v2/{214.723affb37c13bf7a.js → 214.79650a549273f163.js} +1 -1
  4. package/dist/client-v2/264.718a107e43fc163c.js +10 -0
  5. package/dist/client-v2/373.f5d5292e53c4e832.js +10 -0
  6. package/dist/client-v2/{41.1805b2edfaa4afe2.js → 41.ba6e080cc0488143.js} +1 -1
  7. package/dist/client-v2/418.29e713f79131eece.js +10 -0
  8. package/dist/client-v2/619.bd3c5698b40705c3.js +10 -0
  9. package/dist/client-v2/677.a991ce0250ff5c77.js +10 -0
  10. package/dist/client-v2/{70.a15d7fcec7c41768.js → 70.bda9518881c05360.js} +1 -1
  11. package/dist/client-v2/925.f5370de8f6632d65.js +10 -0
  12. package/dist/client-v2/index.js +1 -1
  13. package/dist/externalVersion.js +7 -10
  14. package/dist/locale/en-US.json +94 -25
  15. package/dist/locale/vi-VN.json +94 -25
  16. package/dist/locale/zh-CN.json +94 -25
  17. package/dist/server/collections/agent-execution-spans.js +37 -0
  18. package/dist/server/collections/agent-harness-profiles.js +2 -2
  19. package/dist/server/collections/agent-memory-contexts.js +125 -0
  20. package/dist/server/collections/orchestrator-logs.js +2 -2
  21. package/dist/server/migrations/20260425000000-add-interaction-schema.js +3 -1
  22. package/dist/server/migrations/20260427000000-change-packages-to-text.js +3 -1
  23. package/dist/server/migrations/20260427000001-change-other-json-to-text.js +6 -2
  24. package/dist/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.js +21 -19
  25. package/dist/server/migrations/20260621000000-native-policy-profile-defaults.js +193 -0
  26. package/dist/server/plugin.js +128 -74
  27. package/dist/server/resources/agent-monitor.js +454 -0
  28. package/dist/server/services/AgentHarness.js +24 -499
  29. package/dist/server/services/AgentMemoryContextService.js +216 -0
  30. package/dist/server/services/ExecutionSpanService.js +2 -2
  31. package/dist/server/services/NativeSubAgentObserver.js +413 -0
  32. package/dist/server/skill-hub/plugin.js +81 -5
  33. package/dist/server/skill-hub/tasks/SkillExecutionTask.js +9 -3
  34. package/dist/server/tools/delegate-task.js +11 -589
  35. package/dist/server/utils/skill-settings.js +18 -1
  36. package/package.json +47 -49
  37. package/src/client/AIEmployeesContext.tsx +5 -18
  38. package/src/client/AgentRunsTab.tsx +2 -771
  39. package/src/client/HarnessProfilesTab.tsx +2 -257
  40. package/src/client/OrchestratorSettings.tsx +97 -106
  41. package/src/client/RulesTab.tsx +2 -788
  42. package/src/client/plugin.tsx +0 -2
  43. package/src/client/skill-hub/components/ExecutionHistory.tsx +200 -202
  44. package/src/client/skill-hub/components/ExecutionProgress.tsx +51 -55
  45. package/src/client/skill-hub/components/LoopSettings.tsx +331 -331
  46. package/src/client/skill-hub/components/SkillEditor.tsx +43 -39
  47. package/src/client/skill-hub/components/SkillManager.tsx +194 -181
  48. package/src/client/skill-hub/components/SkillTestPanel.tsx +141 -145
  49. package/src/client/skill-hub/locale.ts +16 -16
  50. package/src/client/skill-hub/tools/SkillHubCard.tsx +104 -109
  51. package/src/client/skill-hub/tools/loopTemplates.ts +52 -52
  52. package/src/client/skill-hub/utils/jsonFields.ts +7 -3
  53. package/src/client-v2/components/AIEmployeesContext.tsx +3 -16
  54. package/src/client-v2/components/AgentRunsTab.tsx +182 -455
  55. package/src/client-v2/components/HarnessProfilesTab.tsx +34 -31
  56. package/src/client-v2/components/RulesTab.tsx +2 -782
  57. package/src/client-v2/components/TracingTab.tsx +1 -1
  58. package/src/client-v2/hooks/useApiRequest.ts +8 -1
  59. package/src/client-v2/pages/RulesPage.tsx +2 -2
  60. package/src/client-v2/plugin.tsx +3 -3
  61. package/src/locale/en-US.json +94 -25
  62. package/src/locale/vi-VN.json +94 -25
  63. package/src/locale/zh-CN.json +94 -25
  64. package/src/server/__tests__/native-sub-agent-observer.test.ts +246 -0
  65. package/src/server/__tests__/skill-settings.test.ts +6 -6
  66. package/src/server/__tests__/smoke.test.ts +1 -0
  67. package/src/server/collections/agent-execution-spans.ts +37 -0
  68. package/src/server/collections/agent-harness-profiles.ts +59 -59
  69. package/src/server/collections/agent-loop-events.ts +71 -71
  70. package/src/server/collections/agent-loop-steps.ts +144 -144
  71. package/src/server/collections/agent-memory-contexts.ts +95 -0
  72. package/src/server/collections/orchestrator-logs.ts +4 -4
  73. package/src/server/collections/skill-definitions.ts +111 -111
  74. package/src/server/collections/skill-executions.ts +106 -106
  75. package/src/server/collections/skill-loop-configs.ts +65 -65
  76. package/src/server/migrations/20260423000000-add-progress-fields.ts +14 -14
  77. package/src/server/migrations/20260425000000-add-interaction-schema.ts +3 -1
  78. package/src/server/migrations/20260427000000-change-packages-to-text.ts +4 -2
  79. package/src/server/migrations/20260427000001-change-other-json-to-text.ts +9 -5
  80. package/src/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.ts +30 -30
  81. package/src/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.ts +145 -142
  82. package/src/server/migrations/20260615000000-normalize-ai-employee-tool-bindings.ts +2 -2
  83. package/src/server/migrations/20260621000000-native-policy-profile-defaults.ts +193 -0
  84. package/src/server/plugin.ts +151 -94
  85. package/src/server/resources/agent-monitor.ts +482 -0
  86. package/src/server/services/AgentHarness.ts +38 -623
  87. package/src/server/services/AgentMemoryContextService.ts +256 -0
  88. package/src/server/services/AgentPlanValidator.ts +73 -73
  89. package/src/server/services/ExecutionSpanService.ts +6 -2
  90. package/src/server/services/FileManager.ts +144 -144
  91. package/src/server/services/NativeSubAgentObserver.ts +507 -0
  92. package/src/server/services/SkillManager.ts +583 -583
  93. package/src/server/services/SkillRepositoryService.ts +5 -7
  94. package/src/server/services/TokenTracker.ts +3 -3
  95. package/src/server/services/WorkerEnvManager.ts +1 -2
  96. package/src/server/skill-hub/actions/git-import.ts +5 -7
  97. package/src/server/skill-hub/plugin.ts +89 -6
  98. package/src/server/skill-hub/tasks/SkillExecutionTask.ts +470 -460
  99. package/src/server/skill-hub/utils/json-fields.ts +1 -1
  100. package/src/server/tools/delegate-task.ts +13 -847
  101. package/src/server/utils/skill-settings.ts +24 -6
  102. package/dist/client-v2/264.0533912e6c5ea2d7.js +0 -10
  103. package/dist/client-v2/418.5ae055abf141820e.js +0 -10
  104. package/dist/client-v2/619.d99d3c9e61c99064.js +0 -10
  105. package/dist/client-v2/892.72db4161511c8a16.js +0 -10
  106. package/dist/client-v2/926.87f660b670d85bcc.js +0 -10
  107. package/src/client/tools/PlanApprovalCard.tsx +0 -176
  108. package/src/client/tools/registerOrchestratorCards.ts +0 -17
@@ -1,771 +1,2 @@
1
- import React, { useMemo, useState } from 'react';
2
- import {
3
- Alert,
4
- Button,
5
- Card,
6
- Collapse,
7
- Descriptions,
8
- Drawer,
9
- Empty,
10
- Form,
11
- message,
12
- Popconfirm,
13
- Select,
14
- Space,
15
- Spin,
16
- Table,
17
- Tag,
18
- Timeline,
19
- Typography,
20
- } from 'antd';
21
- import {
22
- BranchesOutlined,
23
- CheckCircleOutlined,
24
- ClockCircleOutlined,
25
- CloseCircleOutlined,
26
- EyeOutlined,
27
- PauseCircleOutlined,
28
- PlayCircleOutlined,
29
- RedoOutlined,
30
- ReloadOutlined,
31
- StopOutlined,
32
- } from '@ant-design/icons';
33
- import { useRequest } from 'ahooks';
34
- import { useApp } from '@nocobase/client-v2';
35
- import { useAIEmployees } from './AIEmployeesContext';
36
- import { parseJsonText } from './skill-hub/utils/jsonFields';
37
-
38
- const { Paragraph, Text } = Typography;
39
-
40
- type FilterState = {
41
- leader?: string;
42
- status?: string;
43
- };
44
-
45
- const terminalRunStatuses = new Set(['succeeded', 'failed', 'rejected', 'canceled']);
46
-
47
- function statusColor(status?: string) {
48
- switch (status) {
49
- case 'succeeded':
50
- case 'success':
51
- return 'success';
52
- case 'failed':
53
- case 'error':
54
- return 'error';
55
- case 'waiting_user':
56
- case 'waiting_plan_approval':
57
- case 'needs_replan':
58
- return 'warning';
59
- case 'approved':
60
- case 'running':
61
- case 'planning':
62
- return 'processing';
63
- case 'rejected':
64
- case 'canceled':
65
- case 'skipped':
66
- return 'default';
67
- default:
68
- return 'default';
69
- }
70
- }
71
-
72
- function statusIcon(status?: string) {
73
- switch (status) {
74
- case 'succeeded':
75
- case 'success':
76
- return <CheckCircleOutlined />;
77
- case 'failed':
78
- case 'error':
79
- return <CloseCircleOutlined />;
80
- case 'waiting_user':
81
- case 'waiting_plan_approval':
82
- case 'needs_replan':
83
- return <PauseCircleOutlined />;
84
- case 'approved':
85
- case 'running':
86
- case 'planning':
87
- return <ClockCircleOutlined />;
88
- default:
89
- return undefined;
90
- }
91
- }
92
-
93
- function timelineColor(status?: string) {
94
- switch (status) {
95
- case 'succeeded':
96
- case 'success':
97
- return 'green';
98
- case 'failed':
99
- case 'error':
100
- return 'red';
101
- case 'waiting_user':
102
- case 'waiting_plan_approval':
103
- case 'needs_replan':
104
- return 'orange';
105
- case 'approved':
106
- case 'running':
107
- case 'planning':
108
- return 'blue';
109
- default:
110
- return 'gray';
111
- }
112
- }
113
-
114
- function StatusTag({ status }: { status?: string }) {
115
- return (
116
- <Tag icon={statusIcon(status)} color={statusColor(status)}>
117
- {status || '-'}
118
- </Tag>
119
- );
120
- }
121
-
122
- function formatDate(value?: string) {
123
- return value ? new Date(value).toLocaleString() : '-';
124
- }
125
-
126
- function formatDuration(start?: string, end?: string) {
127
- if (!start) return '-';
128
- const startMs = new Date(start).getTime();
129
- const endMs = end ? new Date(end).getTime() : Date.now();
130
- const diff = Math.max(0, endMs - startMs);
131
- if (diff >= 60000) return `${Math.round(diff / 60000)}m`;
132
- if (diff >= 1000) return `${(diff / 1000).toFixed(1)}s`;
133
- return `${diff}ms`;
134
- }
135
-
136
- function formatJson(value: any) {
137
- if (value === undefined || value === null || value === '') return '';
138
- if (typeof value === 'string') return value;
139
- try {
140
- return JSON.stringify(value, null, 2);
141
- } catch {
142
- return String(value);
143
- }
144
- }
145
-
146
- function buildSkillFileUrl(execution: any, file: any) {
147
- if (file?.downloadUrl) return file.downloadUrl;
148
- if (!execution?.id || !file?.name) return '';
149
- return `/api/skillHub:download?execId=${execution.id}&filename=${encodeURIComponent(file.name)}`;
150
- }
151
-
152
- function renderSkillFileLink(execution: any, file: any, index: number) {
153
- const url = buildSkillFileUrl(execution, file);
154
- const label = file.name || file.path || `file-${index + 1}`;
155
- return url ? (
156
- <a key={index} href={url} target="_blank" rel="noreferrer">
157
- {label}
158
- </a>
159
- ) : (
160
- <Text key={index}>{label}</Text>
161
- );
162
- }
163
-
164
- function TextBlock({ value, rows = 10 }: { value: any; rows?: number }) {
165
- const text = formatJson(value);
166
- if (!text) return <Text type="secondary">-</Text>;
167
- return (
168
- <Paragraph
169
- style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', margin: 0, fontSize: 12 }}
170
- ellipsis={{ rows, expandable: true }}
171
- >
172
- {text}
173
- </Paragraph>
174
- );
175
- }
176
-
177
- export const AgentRunsTab: React.FC = () => {
178
- const api = useApp().apiClient;
179
- const { employees, employeeMap } = useAIEmployees();
180
- const [filters, setFilters] = useState<FilterState>({});
181
- const [page, setPage] = useState(1);
182
- const [pageSize, setPageSize] = useState(20);
183
- const [selectedRun, setSelectedRun] = useState<any>(null);
184
- const [detail, setDetail] = useState<any>(null);
185
- const [detailLoading, setDetailLoading] = useState(false);
186
- const [actionLoading, setActionLoading] = useState(false);
187
-
188
- const requestParams = useMemo(() => {
189
- const filter: any = {};
190
- if (filters.leader) filter.leaderUsername = filters.leader;
191
- if (filters.status) filter.status = filters.status;
192
- return {
193
- sort: ['-createdAt'],
194
- page,
195
- pageSize,
196
- filter,
197
- };
198
- }, [filters, page, pageSize]);
199
-
200
- const { data, loading, refresh } = useRequest(
201
- () =>
202
- api.request({
203
- url: 'agentLoops:list',
204
- params: requestParams,
205
- }),
206
- {
207
- refreshDeps: [requestParams],
208
- },
209
- );
210
-
211
- const runs = useMemo(() => {
212
- const raw = (data as any)?.data ?? data;
213
- if (Array.isArray(raw)) return raw;
214
- return Array.isArray(raw?.data) ? raw.data : [];
215
- }, [data]);
216
-
217
- const total = useMemo(() => {
218
- const raw = (data as any)?.data ?? data;
219
- const count = raw?.meta?.count ?? (data as any)?.meta?.count;
220
- return typeof count === 'number' ? count : 0;
221
- }, [data]);
222
-
223
- const employeeOptions = useMemo(
224
- () =>
225
- employees.map((employee) => ({
226
- label: employee.nickname || employee.username,
227
- value: employee.username,
228
- })),
229
- [employees],
230
- );
231
-
232
- const fetchDetail = async (runId: string | number, seed?: any) => {
233
- setSelectedRun(seed || selectedRun || { id: runId });
234
- setDetailLoading(true);
235
- try {
236
- const res = await api.request({
237
- url: 'agentLoops:get',
238
- params: { filterByTk: runId },
239
- });
240
- setDetail((res as any)?.data?.data || (res as any)?.data || null);
241
- } finally {
242
- setDetailLoading(false);
243
- }
244
- };
245
-
246
- const updateFilter = (patch: Partial<FilterState>) => {
247
- setFilters((prev) => ({ ...prev, ...patch }));
248
- setPage(1);
249
- };
250
-
251
- const resetFilters = () => {
252
- setFilters({});
253
- setPage(1);
254
- };
255
-
256
- const runAction = async (action: 'cancel' | 'resume', runId: string | number) => {
257
- setActionLoading(true);
258
- try {
259
- await api.request({
260
- url: action === 'cancel' ? 'agentLoops:cancel' : 'agentLoops:resume',
261
- method: 'POST',
262
- data: action === 'cancel' ? { runId } : { runId, stepId: detail?.run?.currentStepId, approved: true },
263
- });
264
- message.success(action === 'cancel' ? 'Run canceled' : 'Run resumed');
265
- refresh();
266
- await fetchDetail(runId);
267
- } catch (error: any) {
268
- message.error(error?.message || `Failed to ${action} run`);
269
- } finally {
270
- setActionLoading(false);
271
- }
272
- };
273
-
274
- const retryStep = async (stepId: string | number) => {
275
- const runId = detail?.run?.id || selectedRun?.id;
276
- setActionLoading(true);
277
- try {
278
- await api.request({
279
- url: 'agentLoops:retryStep',
280
- method: 'POST',
281
- data: { stepId },
282
- });
283
- message.success('Step queued for retry');
284
- refresh();
285
- if (runId) await fetchDetail(runId);
286
- } catch (error: any) {
287
- message.error(error?.message || 'Failed to retry step');
288
- } finally {
289
- setActionLoading(false);
290
- }
291
- };
292
-
293
- const columns = [
294
- {
295
- title: 'Time',
296
- dataIndex: 'createdAt',
297
- key: 'createdAt',
298
- width: 170,
299
- render: formatDate,
300
- },
301
- {
302
- title: 'Leader',
303
- dataIndex: 'leaderUsername',
304
- key: 'leaderUsername',
305
- width: 160,
306
- render: (username: string) => <Tag color="blue">{employeeMap.get(username) || username || '-'}</Tag>,
307
- },
308
- {
309
- title: 'Goal',
310
- dataIndex: 'goal',
311
- key: 'goal',
312
- render: (goal: string) => (
313
- <Text ellipsis style={{ maxWidth: 380 }}>
314
- {goal || '-'}
315
- </Text>
316
- ),
317
- },
318
- {
319
- title: 'Status',
320
- dataIndex: 'status',
321
- key: 'status',
322
- width: 130,
323
- render: (status: string) => <StatusTag status={status} />,
324
- },
325
- {
326
- title: 'Iterations',
327
- dataIndex: 'iterationCount',
328
- key: 'iterationCount',
329
- width: 90,
330
- render: (value: number) => value ?? 0,
331
- },
332
- {
333
- title: 'Duration',
334
- key: 'duration',
335
- width: 100,
336
- render: (_: any, record: any) => formatDuration(record.startedAt || record.createdAt, record.endedAt),
337
- },
338
- {
339
- title: '',
340
- key: 'actions',
341
- width: 90,
342
- render: (_: any, record: any) => (
343
- <Button type="link" size="small" icon={<EyeOutlined />} onClick={() => fetchDetail(record.id, record)}>
344
- Detail
345
- </Button>
346
- ),
347
- },
348
- ];
349
-
350
- const steps = Array.isArray(detail?.steps) ? detail.steps : [];
351
- const events = Array.isArray(detail?.events) ? detail.events : [];
352
- const spans = Array.isArray(detail?.spans) ? detail.spans : [];
353
- const skillExecutions = Array.isArray(detail?.skillExecutions) ? detail.skillExecutions : [];
354
- const run = detail?.run || selectedRun;
355
- const hasFilters = Boolean(filters.leader || filters.status);
356
-
357
- const stepColumns = [
358
- {
359
- title: '#',
360
- dataIndex: 'index',
361
- key: 'index',
362
- width: 56,
363
- render: (value: number) => Number(value ?? 0) + 1,
364
- },
365
- {
366
- title: 'Status',
367
- dataIndex: 'status',
368
- key: 'status',
369
- width: 130,
370
- render: (status: string) => <StatusTag status={status} />,
371
- },
372
- {
373
- title: 'Type',
374
- dataIndex: 'type',
375
- key: 'type',
376
- width: 110,
377
- render: (type: string) => <Tag>{type}</Tag>,
378
- },
379
- {
380
- title: 'Step',
381
- key: 'step',
382
- render: (_: any, record: any) => (
383
- <Space direction="vertical" size={2} style={{ width: '100%' }}>
384
- <Text strong>{record.title || record.planKey}</Text>
385
- {record.description && <Text type="secondary">{record.description}</Text>}
386
- {record.target && <Text code>{record.target}</Text>}
387
- </Space>
388
- ),
389
- },
390
- {
391
- title: 'Depends',
392
- dataIndex: 'dependsOn',
393
- key: 'dependsOn',
394
- width: 130,
395
- render: (dependsOn: string[]) =>
396
- Array.isArray(dependsOn) && dependsOn.length ? dependsOn.map((key) => <Tag key={key}>{key}</Tag>) : '-',
397
- },
398
- {
399
- title: 'Attempts',
400
- key: 'attempts',
401
- width: 90,
402
- render: (_: any, record: any) => `${record.attempt || 0}/${record.maxAttempts || 0}`,
403
- },
404
- {
405
- title: '',
406
- key: 'actions',
407
- width: 90,
408
- render: (_: any, record: any) =>
409
- record.status === 'failed' && Number(record.attempt || 0) < Number(record.maxAttempts || 0) ? (
410
- <Button
411
- type="link"
412
- size="small"
413
- icon={<RedoOutlined />}
414
- loading={actionLoading}
415
- onClick={() => retryStep(record.id)}
416
- >
417
- Retry
418
- </Button>
419
- ) : null,
420
- },
421
- ];
422
-
423
- return (
424
- <div>
425
- <Alert
426
- type="info"
427
- showIcon
428
- style={{ marginBottom: 16 }}
429
- message="Agent Runs"
430
- description={
431
- <Text type="secondary">
432
- Persistent loop runs created by the orchestrator tools. Each run stores the goal, plan, step state,
433
- approvals, and linked Skill Hub or sub-agent traces.
434
- </Text>
435
- }
436
- />
437
-
438
- <Card bordered={false}>
439
- <Form layout="inline" style={{ marginBottom: 16, rowGap: 8, flexWrap: 'wrap' }}>
440
- <Form.Item label="Leader">
441
- <Select
442
- allowClear
443
- showSearch
444
- optionFilterProp="label"
445
- placeholder="Any leader"
446
- style={{ minWidth: 180 }}
447
- options={employeeOptions}
448
- value={filters.leader}
449
- onChange={(value) => updateFilter({ leader: value })}
450
- />
451
- </Form.Item>
452
- <Form.Item label="Status">
453
- <Select
454
- allowClear
455
- placeholder="Any status"
456
- style={{ minWidth: 160 }}
457
- value={filters.status}
458
- onChange={(value) => updateFilter({ status: value })}
459
- options={[
460
- { label: 'Planning', value: 'planning' },
461
- { label: 'Waiting plan approval', value: 'waiting_plan_approval' },
462
- { label: 'Approved', value: 'approved' },
463
- { label: 'Running', value: 'running' },
464
- { label: 'Waiting user', value: 'waiting_user' },
465
- { label: 'Needs replan', value: 'needs_replan' },
466
- { label: 'Succeeded', value: 'succeeded' },
467
- { label: 'Failed', value: 'failed' },
468
- { label: 'Rejected', value: 'rejected' },
469
- { label: 'Canceled', value: 'canceled' },
470
- ]}
471
- />
472
- </Form.Item>
473
- <Form.Item>
474
- <Space>
475
- <Button onClick={resetFilters} disabled={!hasFilters}>
476
- Reset
477
- </Button>
478
- <Button icon={<ReloadOutlined />} onClick={refresh}>
479
- Refresh
480
- </Button>
481
- </Space>
482
- </Form.Item>
483
- </Form>
484
-
485
- <Table
486
- rowKey="id"
487
- loading={loading}
488
- dataSource={runs}
489
- columns={columns}
490
- scroll={{ x: 'max-content' }}
491
- pagination={{
492
- current: page,
493
- pageSize,
494
- total,
495
- showSizeChanger: true,
496
- pageSizeOptions: [10, 20, 50, 100],
497
- showTotal: (count) => `${count} run${count === 1 ? '' : 's'}`,
498
- onChange: (nextPage, nextSize) => {
499
- setPage(nextPage);
500
- if (nextSize && nextSize !== pageSize) setPageSize(nextSize);
501
- },
502
- }}
503
- locale={{
504
- emptyText: <Empty description={hasFilters ? 'No runs match the current filters' : 'No agent runs yet'} />,
505
- }}
506
- />
507
- </Card>
508
-
509
- <Drawer
510
- title="Agent Run Detail"
511
- width={980}
512
- onClose={() => {
513
- setSelectedRun(null);
514
- setDetail(null);
515
- }}
516
- open={!!selectedRun}
517
- >
518
- {run && (
519
- <Spin spinning={detailLoading}>
520
- <Space direction="vertical" size={16} style={{ width: '100%' }}>
521
- <Space wrap>
522
- <Button icon={<ReloadOutlined />} onClick={() => fetchDetail(run.id)} loading={detailLoading}>
523
- Refresh
524
- </Button>
525
- {run.status === 'waiting_user' && (
526
- <Button
527
- type="primary"
528
- icon={<PlayCircleOutlined />}
529
- loading={actionLoading}
530
- onClick={() => runAction('resume', run.id)}
531
- >
532
- Resume
533
- </Button>
534
- )}
535
- {!terminalRunStatuses.has(run.status) && (
536
- <Popconfirm title="Cancel this run?" onConfirm={() => runAction('cancel', run.id)}>
537
- <Button danger icon={<StopOutlined />} loading={actionLoading}>
538
- Cancel run
539
- </Button>
540
- </Popconfirm>
541
- )}
542
- </Space>
543
-
544
- <Descriptions bordered size="small" column={2}>
545
- <Descriptions.Item label="Status">
546
- <StatusTag status={run.status} />
547
- </Descriptions.Item>
548
- <Descriptions.Item label="Leader">
549
- <Tag color="blue">{employeeMap.get(run.leaderUsername) || run.leaderUsername || '-'}</Tag>
550
- </Descriptions.Item>
551
- <Descriptions.Item label="Root run">
552
- <Text code>{run.rootRunId || '-'}</Text>
553
- </Descriptions.Item>
554
- <Descriptions.Item label="Current step">
555
- <Text code>{run.currentStepId || '-'}</Text>
556
- </Descriptions.Item>
557
- <Descriptions.Item label="Iterations">{run.iterationCount || 0}</Descriptions.Item>
558
- <Descriptions.Item label="Approval">{run.approvalStatus || '-'}</Descriptions.Item>
559
- <Descriptions.Item label="Plan version">{run.planVersion || '-'}</Descriptions.Item>
560
- <Descriptions.Item label="Harness">{run.metadata?.harnessTag || '-'}</Descriptions.Item>
561
- <Descriptions.Item label="Duration">
562
- {formatDuration(run.startedAt || run.createdAt, run.endedAt)}
563
- </Descriptions.Item>
564
- <Descriptions.Item label="Started">{formatDate(run.startedAt || run.createdAt)}</Descriptions.Item>
565
- <Descriptions.Item label="Ended">{formatDate(run.endedAt)}</Descriptions.Item>
566
- </Descriptions>
567
-
568
- <Card title="Goal" size="small">
569
- <TextBlock value={run.goal} rows={6} />
570
- </Card>
571
-
572
- <Card title="Plan" size="small" extra={<BranchesOutlined />}>
573
- <Table
574
- rowKey="id"
575
- size="small"
576
- dataSource={steps}
577
- columns={stepColumns}
578
- pagination={false}
579
- scroll={{ x: 'max-content' }}
580
- expandable={{
581
- expandedRowRender: (record: any) => (
582
- <Space direction="vertical" size={12} style={{ width: '100%' }}>
583
- <Card size="small" title="Input">
584
- <TextBlock value={record.input} rows={8} />
585
- </Card>
586
- <Card size="small" title="Output">
587
- <TextBlock value={record.output} rows={8} />
588
- </Card>
589
- {record.approval && Object.keys(record.approval).length > 0 && (
590
- <Card size="small" title="Approval">
591
- <TextBlock value={record.approval} rows={8} />
592
- </Card>
593
- )}
594
- {record.error && (
595
- <Card size="small" title="Error" style={{ borderColor: '#ffa39e' }}>
596
- <TextBlock value={record.error} rows={8} />
597
- </Card>
598
- )}
599
- </Space>
600
- ),
601
- }}
602
- locale={{ emptyText: <Empty description="No plan steps" /> }}
603
- />
604
- </Card>
605
-
606
- <Card title="Event Timeline" size="small">
607
- {events.length ? (
608
- <Timeline
609
- items={events.map((event: any) => ({
610
- key: event.id,
611
- color: timelineColor(event.status),
612
- children: (
613
- <Space direction="vertical" size={2} style={{ width: '100%' }}>
614
- <Space wrap>
615
- <Text strong>{event.title || event.type}</Text>
616
- <StatusTag status={event.status} />
617
- {event.stepId && <Text type="secondary">step #{event.stepId}</Text>}
618
- </Space>
619
- <Text type="secondary">{formatDate(event.createdAt)}</Text>
620
- {event.content && <TextBlock value={event.content} rows={4} />}
621
- </Space>
622
- ),
623
- }))}
624
- />
625
- ) : (
626
- <Empty description="No events captured" />
627
- )}
628
- </Card>
629
-
630
- <Collapse
631
- items={[
632
- {
633
- key: 'spans',
634
- label: `Linked spans (${spans.length})`,
635
- children: spans.length ? (
636
- <Table
637
- rowKey="id"
638
- size="small"
639
- dataSource={spans}
640
- pagination={false}
641
- scroll={{ x: 'max-content' }}
642
- columns={[
643
- {
644
- title: 'Type',
645
- dataIndex: 'type',
646
- key: 'type',
647
- width: 110,
648
- render: (value: string) => <Tag>{value}</Tag>,
649
- },
650
- {
651
- title: 'Status',
652
- dataIndex: 'status',
653
- key: 'status',
654
- width: 110,
655
- render: (value: string) => <StatusTag status={value} />,
656
- },
657
- { title: 'Title', dataIndex: 'title', key: 'title' },
658
- {
659
- title: 'Tool',
660
- dataIndex: 'toolName',
661
- key: 'toolName',
662
- render: (value: string) => (value ? <Text code>{value}</Text> : '-'),
663
- },
664
- {
665
- title: 'Duration',
666
- dataIndex: 'durationMs',
667
- key: 'durationMs',
668
- width: 100,
669
- render: (value: number) => (value ? `${value}ms` : '-'),
670
- },
671
- {
672
- title: 'Skill Exec',
673
- dataIndex: 'skillExecutionId',
674
- key: 'skillExecutionId',
675
- width: 110,
676
- render: (value: any) => value || '-',
677
- },
678
- ]}
679
- />
680
- ) : (
681
- <Empty description="No linked spans" />
682
- ),
683
- },
684
- {
685
- key: 'skills',
686
- label: `Skill executions (${skillExecutions.length})`,
687
- children: skillExecutions.length ? (
688
- <Space direction="vertical" size={12} style={{ width: '100%' }}>
689
- {skillExecutions.map((execution: any) => {
690
- const files = parseJsonText<any[]>(execution.outputFiles, []);
691
- return (
692
- <Card
693
- key={execution.id}
694
- size="small"
695
- title={
696
- <Space wrap>
697
- <Text>Skill execution #{execution.id}</Text>
698
- <StatusTag status={execution.status} />
699
- {execution.agentLoopStepId && (
700
- <Text type="secondary">step #{execution.agentLoopStepId}</Text>
701
- )}
702
- </Space>
703
- }
704
- >
705
- <Space direction="vertical" size={8} style={{ width: '100%' }}>
706
- <Descriptions size="small" column={2}>
707
- <Descriptions.Item label="Duration">
708
- {execution.durationMs ? `${execution.durationMs}ms` : '-'}
709
- </Descriptions.Item>
710
- <Descriptions.Item label="Created">
711
- {formatDate(execution.createdAt)}
712
- </Descriptions.Item>
713
- </Descriptions>
714
- <Collapse
715
- size="small"
716
- items={[
717
- {
718
- key: 'stdout',
719
- label: 'stdout',
720
- children: <TextBlock value={execution.stdout} rows={10} />,
721
- },
722
- {
723
- key: 'stderr',
724
- label: 'stderr',
725
- children: <TextBlock value={execution.stderr} rows={10} />,
726
- },
727
- {
728
- key: 'files',
729
- label: `files (${files.length})`,
730
- children: files.length ? (
731
- <Space direction="vertical">
732
- {files.map((file: any, index: number) =>
733
- renderSkillFileLink(execution, file, index),
734
- )}
735
- </Space>
736
- ) : (
737
- <Empty description="No files" />
738
- ),
739
- },
740
- ]}
741
- />
742
- </Space>
743
- </Card>
744
- );
745
- })}
746
- </Space>
747
- ) : (
748
- <Empty description="No linked skill executions" />
749
- ),
750
- },
751
- ]}
752
- />
753
-
754
- {(run.finalAnswer || run.summary) && (
755
- <Card title="Final Output" size="small">
756
- {run.summary && (
757
- <Paragraph style={{ whiteSpace: 'pre-wrap', marginBottom: 12 }}>
758
- <Text strong>Summary: </Text>
759
- {run.summary}
760
- </Paragraph>
761
- )}
762
- <TextBlock value={run.finalAnswer} rows={16} />
763
- </Card>
764
- )}
765
- </Space>
766
- </Spin>
767
- )}
768
- </Drawer>
769
- </div>
770
- );
771
- };
1
+ export { AgentRunsTab } from '../client-v2/components/AgentRunsTab';
2
+ export { AgentRunsTab as default } from '../client-v2/components/AgentRunsTab';