plugin-agent-orchestrator 1.0.21 → 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.
Files changed (180) hide show
  1. package/client-v2.d.ts +2 -0
  2. package/client-v2.js +1 -0
  3. package/dist/client/index.js +1 -1
  4. package/dist/client-v2/214.723affb37c13bf7a.js +10 -0
  5. package/dist/client-v2/264.0533912e6c5ea2d7.js +10 -0
  6. package/dist/client-v2/41.1805b2edfaa4afe2.js +10 -0
  7. package/dist/client-v2/418.5ae055abf141820e.js +10 -0
  8. package/dist/client-v2/619.d99d3c9e61c99064.js +10 -0
  9. package/dist/client-v2/70.a15d7fcec7c41768.js +10 -0
  10. package/dist/client-v2/892.72db4161511c8a16.js +10 -0
  11. package/dist/client-v2/926.87f660b670d85bcc.js +10 -0
  12. package/dist/client-v2/index.js +10 -0
  13. package/dist/externalVersion.js +7 -6
  14. package/dist/locale/en-US.json +7 -0
  15. package/dist/locale/vi-VN.json +7 -0
  16. package/dist/locale/zh-CN.json +27 -0
  17. package/dist/server/migrations/20260615000000-normalize-ai-employee-tool-bindings.js +63 -0
  18. package/dist/server/plugin.js +41 -1
  19. package/dist/server/services/AgentHarness.js +52 -27
  20. package/dist/server/services/AgentLoopController.js +8 -2
  21. package/dist/server/services/AgentLoopService.js +1 -1
  22. package/dist/server/services/AgentRegistryService.js +53 -42
  23. package/dist/server/services/CircuitBreaker.js +7 -2
  24. package/dist/server/services/CodeValidator.js +48 -14
  25. package/dist/server/services/SandboxRunner.js +18 -14
  26. package/dist/server/skill-hub/plugin.js +44 -17
  27. package/dist/server/tools/delegate-task.js +7 -2
  28. package/dist/server/tools/skill-execute.js +33 -2
  29. package/dist/server/utils/ai-manager.js +51 -0
  30. package/dist/server/utils/ctx-utils.js +11 -0
  31. package/dist/server/utils/skill-settings.js +122 -0
  32. package/package.json +49 -45
  33. package/src/client/AIEmployeesContext.tsx +51 -14
  34. package/src/client/AgentRunsTab.tsx +767 -764
  35. package/src/client/HarnessProfilesTab.tsx +254 -247
  36. package/src/client/RulesTab.tsx +780 -716
  37. package/src/client/TracingTab.tsx +1 -0
  38. package/src/client/plugin.tsx +34 -27
  39. package/src/client/skill-hub/components/GitSkillImport.tsx +10 -3
  40. package/src/client/skill-hub/components/SkillMetrics.tsx +157 -124
  41. package/src/client/skill-hub/index.tsx +58 -51
  42. package/src/client/skill-hub/tools/InteractionSchemasProvider.tsx +132 -99
  43. package/src/client/skill-hub/tools/registerSkillLoopCards.ts +71 -58
  44. package/src/client/tools/registerOrchestratorCards.ts +17 -7
  45. package/src/client-v2/components/AIEmployeeSelect.tsx +47 -0
  46. package/src/client-v2/components/AIEmployeesContext.tsx +110 -0
  47. package/src/client-v2/components/AgentRunsTab.tsx +767 -0
  48. package/src/client-v2/components/HarnessProfilesTab.tsx +254 -0
  49. package/src/client-v2/components/RulesTab.tsx +782 -0
  50. package/src/client-v2/components/TracingTab.tsx +432 -0
  51. package/src/client-v2/hooks/useApiRequest.ts +114 -0
  52. package/src/client-v2/pages/AgentRunsPage.tsx +13 -0
  53. package/src/client-v2/pages/ExecutionHistoryPage.tsx +10 -0
  54. package/src/client-v2/pages/HarnessProfilesPage.tsx +10 -0
  55. package/src/client-v2/pages/LoopSettingsPage.tsx +10 -0
  56. package/src/client-v2/pages/RulesPage.tsx +13 -0
  57. package/src/client-v2/pages/SkillDefinitionsPage.tsx +10 -0
  58. package/src/client-v2/pages/SkillMetricsPage.tsx +10 -0
  59. package/src/client-v2/pages/TracingPage.tsx +13 -0
  60. package/src/client-v2/plugin.tsx +70 -0
  61. package/src/client-v2/skill-hub/components/ExecutionHistory.tsx +196 -0
  62. package/src/client-v2/skill-hub/components/FileLinkList.tsx +37 -0
  63. package/src/client-v2/skill-hub/components/GitSkillImport.tsx +539 -0
  64. package/src/client-v2/skill-hub/components/LoopSettings.tsx +331 -0
  65. package/src/client-v2/skill-hub/components/SkillEditor.tsx +453 -0
  66. package/src/client-v2/skill-hub/components/SkillManager.tsx +174 -0
  67. package/src/client-v2/skill-hub/components/SkillMetrics.tsx +157 -0
  68. package/src/client-v2/skill-hub/components/SkillTestPanel.tsx +135 -0
  69. package/src/client-v2/skill-hub/locale.ts +13 -0
  70. package/src/client-v2/skill-hub/tools/loopTemplates.ts +52 -0
  71. package/src/client-v2/skill-hub/utils/jsonFields.ts +41 -0
  72. package/src/client-v2/utils/jsonFields.ts +41 -0
  73. package/src/locale/en-US.json +7 -0
  74. package/src/locale/vi-VN.json +7 -0
  75. package/src/locale/zh-CN.json +27 -0
  76. package/src/server/__tests__/agent-registry-service.test.ts +147 -0
  77. package/src/server/__tests__/code-validator.test.ts +63 -0
  78. package/src/server/__tests__/skill-execute.test.ts +33 -0
  79. package/src/server/__tests__/skill-settings.test.ts +63 -0
  80. package/src/server/migrations/20260615000000-normalize-ai-employee-tool-bindings.ts +39 -0
  81. package/src/server/plugin.ts +68 -12
  82. package/src/server/services/AgentHarness.ts +49 -22
  83. package/src/server/services/AgentLoopController.ts +17 -6
  84. package/src/server/services/AgentLoopService.ts +1 -1
  85. package/src/server/services/AgentPlannerService.ts +10 -0
  86. package/src/server/services/AgentRegistryService.ts +89 -47
  87. package/src/server/services/CircuitBreaker.ts +10 -0
  88. package/src/server/services/CodeValidator.ts +237 -159
  89. package/src/server/services/SandboxRunner.ts +203 -189
  90. package/src/server/skill-hub/plugin.ts +933 -898
  91. package/src/server/tools/delegate-task.ts +12 -9
  92. package/src/server/tools/skill-execute.ts +194 -160
  93. package/src/server/utils/ai-manager.ts +24 -0
  94. package/src/server/utils/ctx-utils.ts +14 -0
  95. package/src/server/utils/skill-settings.ts +116 -0
  96. package/dist/client/AIEmployeeSelect.d.ts +0 -11
  97. package/dist/client/AIEmployeesContext.d.ts +0 -30
  98. package/dist/client/AgentRunsTab.d.ts +0 -2
  99. package/dist/client/HarnessProfilesTab.d.ts +0 -2
  100. package/dist/client/OrchestratorSettings.d.ts +0 -3
  101. package/dist/client/RulesTab.d.ts +0 -2
  102. package/dist/client/TracingTab.d.ts +0 -2
  103. package/dist/client/hooks/useRunEventStream.d.ts +0 -22
  104. package/dist/client/index.d.ts +0 -2
  105. package/dist/client/plugin.d.ts +0 -6
  106. package/dist/client/skill-hub/components/ExecutionHistory.d.ts +0 -2
  107. package/dist/client/skill-hub/components/ExecutionProgress.d.ts +0 -20
  108. package/dist/client/skill-hub/components/GitSkillImport.d.ts +0 -7
  109. package/dist/client/skill-hub/components/LoopSettings.d.ts +0 -2
  110. package/dist/client/skill-hub/components/SkillEditor.d.ts +0 -7
  111. package/dist/client/skill-hub/components/SkillManager.d.ts +0 -2
  112. package/dist/client/skill-hub/components/SkillMetrics.d.ts +0 -2
  113. package/dist/client/skill-hub/components/SkillTestPanel.d.ts +0 -7
  114. package/dist/client/skill-hub/index.d.ts +0 -11
  115. package/dist/client/skill-hub/locale.d.ts +0 -3
  116. package/dist/client/skill-hub/tools/InteractionSchemasProvider.d.ts +0 -6
  117. package/dist/client/skill-hub/tools/SkillHubCard.d.ts +0 -3
  118. package/dist/client/skill-hub/tools/loopTemplates.d.ts +0 -22
  119. package/dist/client/skill-hub/tools/registerSkillLoopCards.d.ts +0 -1
  120. package/dist/client/skill-hub/utils/jsonFields.d.ts +0 -3
  121. package/dist/client/tools/PlanApprovalCard.d.ts +0 -3
  122. package/dist/client/tools/registerOrchestratorCards.d.ts +0 -1
  123. package/dist/index.d.ts +0 -2
  124. package/dist/server/collections/agent-execution-spans.d.ts +0 -9
  125. package/dist/server/collections/agent-harness-profiles.d.ts +0 -2
  126. package/dist/server/collections/agent-loop-events.d.ts +0 -2
  127. package/dist/server/collections/agent-loop-runs.d.ts +0 -2
  128. package/dist/server/collections/agent-loop-steps.d.ts +0 -2
  129. package/dist/server/collections/orchestrator-config.d.ts +0 -2
  130. package/dist/server/collections/orchestrator-logs.d.ts +0 -8
  131. package/dist/server/collections/skill-definitions.d.ts +0 -3
  132. package/dist/server/collections/skill-executions.d.ts +0 -3
  133. package/dist/server/collections/skill-loop-configs.d.ts +0 -3
  134. package/dist/server/collections/skill-worker-configs.d.ts +0 -3
  135. package/dist/server/migrations/20260423000000-add-progress-fields.d.ts +0 -4
  136. package/dist/server/migrations/20260425000000-add-interaction-schema.d.ts +0 -4
  137. package/dist/server/migrations/20260427000000-add-tracing-detail-fields.d.ts +0 -7
  138. package/dist/server/migrations/20260427000000-change-packages-to-text.d.ts +0 -4
  139. package/dist/server/migrations/20260427000001-change-other-json-to-text.d.ts +0 -4
  140. package/dist/server/migrations/20260429000000-add-llm-fields.d.ts +0 -7
  141. package/dist/server/migrations/20260429000000-fix-inputargs-json-to-text.d.ts +0 -16
  142. package/dist/server/migrations/20260503000000-add-orchestrator-trace-fields.d.ts +0 -7
  143. package/dist/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.d.ts +0 -7
  144. package/dist/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.d.ts +0 -12
  145. package/dist/server/migrations/20260601000000-add-token-fields.d.ts +0 -7
  146. package/dist/server/plugin.d.ts +0 -16
  147. package/dist/server/resources/agent-loop.d.ts +0 -3
  148. package/dist/server/resources/tracing.d.ts +0 -7
  149. package/dist/server/services/AgentHarness.d.ts +0 -44
  150. package/dist/server/services/AgentLoopController.d.ts +0 -218
  151. package/dist/server/services/AgentLoopRepository.d.ts +0 -20
  152. package/dist/server/services/AgentLoopService.d.ts +0 -159
  153. package/dist/server/services/AgentPlanValidator.d.ts +0 -4
  154. package/dist/server/services/AgentPlannerService.d.ts +0 -8
  155. package/dist/server/services/AgentRegistryService.d.ts +0 -21
  156. package/dist/server/services/CircuitBreaker.d.ts +0 -40
  157. package/dist/server/services/CodeValidator.d.ts +0 -32
  158. package/dist/server/services/ContextAggregator.d.ts +0 -45
  159. package/dist/server/services/ExecutionSpanService.d.ts +0 -46
  160. package/dist/server/services/FileManager.d.ts +0 -28
  161. package/dist/server/services/RunEventBus.d.ts +0 -9
  162. package/dist/server/services/SandboxRunner.d.ts +0 -41
  163. package/dist/server/services/SkillManager.d.ts +0 -6
  164. package/dist/server/services/SkillRepositoryService.d.ts +0 -22
  165. package/dist/server/services/TokenTracker.d.ts +0 -62
  166. package/dist/server/services/WorkerEnvManager.d.ts +0 -26
  167. package/dist/server/skill-hub/actions/git-import.d.ts +0 -21
  168. package/dist/server/skill-hub/mcp/McpController.d.ts +0 -15
  169. package/dist/server/skill-hub/plugin.d.ts +0 -61
  170. package/dist/server/skill-hub/tasks/SkillExecutionTask.d.ts +0 -16
  171. package/dist/server/skill-hub/utils/json-fields.d.ts +0 -7
  172. package/dist/server/tools/agent-loop.d.ts +0 -235
  173. package/dist/server/tools/delegate-task.d.ts +0 -19
  174. package/dist/server/tools/external-rag-search.d.ts +0 -42
  175. package/dist/server/tools/orchestrator-plan.d.ts +0 -205
  176. package/dist/server/tools/skill-execute.d.ts +0 -36
  177. package/dist/server/types.d.ts +0 -47
  178. package/dist/server/utils/ctx-utils.d.ts +0 -30
  179. package/dist/server/utils/logging.d.ts +0 -6
  180. /package/{dist/server/index.d.ts → src/client-v2/index.tsx} +0 -0
@@ -0,0 +1,157 @@
1
+ import React, { useState, useEffect, useCallback, useMemo } from 'react';
2
+ import { Card, Table, Typography, Space, Row, Col, Statistic, Progress } from 'antd';
3
+ import { SyncOutlined, CheckCircleOutlined, CloseCircleOutlined, ClockCircleOutlined } from '@ant-design/icons';
4
+ import { useApiClient as useAPIClient } from '../../hooks/useApiRequest';
5
+ import { useT } from '../locale';
6
+
7
+ const { Title, Text } = Typography;
8
+
9
+ export const SkillMetrics: React.FC = () => {
10
+ const api = useAPIClient();
11
+ const t = useT();
12
+ const [executions, setExecutions] = useState<any[]>([]);
13
+ const [loading, setLoading] = useState(false);
14
+
15
+ const fetchExecutions = useCallback(async () => {
16
+ setLoading(true);
17
+ try {
18
+ // Fetch up to 1000 recent executions to calculate basic metrics
19
+ const { data } = await api.request({
20
+ url: 'skillExecutions:list',
21
+ params: {
22
+ pageSize: 1000,
23
+ sort: ['-createdAt'],
24
+ appends: ['skill'],
25
+ },
26
+ });
27
+ const rawData = data?.data?.data ?? data?.data ?? [];
28
+ setExecutions(Array.isArray(rawData) ? rawData : []);
29
+ } catch {
30
+ // ignore
31
+ } finally {
32
+ setLoading(false);
33
+ }
34
+ }, [api]);
35
+
36
+ useEffect(() => {
37
+ fetchExecutions();
38
+ }, [fetchExecutions]);
39
+
40
+ const metrics = useMemo(() => {
41
+ const total = executions.length;
42
+ const succeeded = executions.filter((e) => e.status === 'succeeded').length;
43
+ const failed = executions.filter((e) => e.status === 'failed').length;
44
+ const timeout = executions.filter((e) => e.status === 'timeout').length;
45
+ const canceled = executions.filter((e) => e.status === 'canceled').length;
46
+
47
+ // Group by skill
48
+ const bySkill: Record<string, any> = {};
49
+ executions.forEach((e) => {
50
+ const skillName = e.skill?.title || e.skill?.name || 'Unknown';
51
+ if (!bySkill[skillName]) {
52
+ bySkill[skillName] = {
53
+ name: skillName,
54
+ total: 0,
55
+ succeeded: 0,
56
+ failed: 0,
57
+ timeout: 0,
58
+ canceled: 0,
59
+ totalDuration: 0,
60
+ durationCount: 0,
61
+ };
62
+ }
63
+ bySkill[skillName].total += 1;
64
+ bySkill[skillName][e.status] = (bySkill[skillName][e.status] || 0) + 1;
65
+ if (e.durationMs) {
66
+ bySkill[skillName].totalDuration += e.durationMs;
67
+ bySkill[skillName].durationCount += 1;
68
+ }
69
+ });
70
+
71
+ const skillData = Object.values(bySkill)
72
+ .map((s) => ({
73
+ ...s,
74
+ successRate: s.total > 0 ? (s.succeeded / s.total) * 100 : 0,
75
+ avgDuration: s.durationCount > 0 ? (s.totalDuration / s.durationCount / 1000).toFixed(2) : 0,
76
+ }))
77
+ .sort((a: any, b: any) => b.total - a.total);
78
+
79
+ return { total, succeeded, failed, timeout, canceled, skillData };
80
+ }, [executions]);
81
+
82
+ const columns = [
83
+ { title: t('Skill'), dataIndex: 'name', key: 'name', width: 200 },
84
+ { title: t('Total Runs'), dataIndex: 'total', key: 'total', width: 100 },
85
+ {
86
+ title: t('Success Rate'),
87
+ dataIndex: 'successRate',
88
+ key: 'successRate',
89
+ width: 150,
90
+ render: (val: number) => (
91
+ <Progress
92
+ percent={Math.round(val)}
93
+ size="small"
94
+ status={val === 100 ? 'success' : val > 50 ? 'active' : 'exception'}
95
+ />
96
+ ),
97
+ },
98
+ { title: t('Success'), dataIndex: 'succeeded', key: 'succeeded', width: 100 },
99
+ { title: t('Failed'), dataIndex: 'failed', key: 'failed', width: 100 },
100
+ { title: t('Timeout'), dataIndex: 'timeout', key: 'timeout', width: 100 },
101
+ { title: t('Avg Duration (s)'), dataIndex: 'avgDuration', key: 'avgDuration', width: 120 },
102
+ ];
103
+
104
+ return (
105
+ <Space direction="vertical" size="large" style={{ width: '100%', padding: '0 16px' }}>
106
+ <Row gutter={16}>
107
+ <Col span={6}>
108
+ <Card size="small">
109
+ <Statistic title={t('Total Executions (Recent)')} value={metrics.total} prefix={<SyncOutlined />} />
110
+ </Card>
111
+ </Col>
112
+ <Col span={6}>
113
+ <Card size="small">
114
+ <Statistic
115
+ title={t('Succeeded')}
116
+ value={metrics.succeeded}
117
+ valueStyle={{ color: '#3f8600' }}
118
+ prefix={<CheckCircleOutlined />}
119
+ />
120
+ </Card>
121
+ </Col>
122
+ <Col span={6}>
123
+ <Card size="small">
124
+ <Statistic
125
+ title={t('Failed')}
126
+ value={metrics.failed}
127
+ valueStyle={{ color: '#cf1322' }}
128
+ prefix={<CloseCircleOutlined />}
129
+ />
130
+ </Card>
131
+ </Col>
132
+ <Col span={6}>
133
+ <Card size="small">
134
+ <Statistic
135
+ title={t('Timeout/Canceled')}
136
+ value={metrics.timeout + metrics.canceled}
137
+ valueStyle={{ color: '#faad14' }}
138
+ prefix={<ClockCircleOutlined />}
139
+ />
140
+ </Card>
141
+ </Col>
142
+ </Row>
143
+
144
+ <Card title={t('Metrics by Skill (Recent)')}>
145
+ <Table
146
+ dataSource={metrics.skillData}
147
+ columns={columns}
148
+ rowKey="name"
149
+ loading={loading}
150
+ pagination={false}
151
+ size="middle"
152
+ scroll={{ x: 'max-content' }}
153
+ />
154
+ </Card>
155
+ </Space>
156
+ );
157
+ };
@@ -0,0 +1,135 @@
1
+ import React, { useState } from 'react';
2
+ import { Modal, Input, Button, Alert, Typography, Space, Spin } from 'antd';
3
+ import { useApiClient as useAPIClient } from '../../hooks/useApiRequest';
4
+ import { useT } from '../locale';
5
+ import { parseJsonText } from '../utils/jsonFields';
6
+ import { FileLinkList } from './FileLinkList';
7
+
8
+ const { TextArea } = Input;
9
+
10
+ interface SkillTestPanelProps {
11
+ skill: any;
12
+ onClose: () => void;
13
+ }
14
+
15
+ export const SkillTestPanel: React.FC<SkillTestPanelProps> = ({ skill, onClose }) => {
16
+ const api = useAPIClient();
17
+ const t = useT();
18
+ const inputSchema = parseJsonText(skill.inputSchema, null);
19
+ const [input, setInput] = useState(
20
+ inputSchema?.properties
21
+ ? JSON.stringify(Object.fromEntries(Object.keys(inputSchema.properties).map((k) => [k, ''])), null, 2)
22
+ : '{}',
23
+ );
24
+ const [running, setRunning] = useState(false);
25
+ const [result, setResult] = useState<any>(null);
26
+ const [error, setError] = useState('');
27
+
28
+ const handleRun = async () => {
29
+ setRunning(true);
30
+ setResult(null);
31
+ setError('');
32
+
33
+ let parsedInput;
34
+ try {
35
+ parsedInput = JSON.parse(input);
36
+ } catch {
37
+ setError(t('Invalid JSON input'));
38
+ setRunning(false);
39
+ return;
40
+ }
41
+
42
+ try {
43
+ const { data } = await api.request({
44
+ url: 'skillHub:test',
45
+ method: 'POST',
46
+ data: { skillId: skill.id, input: parsedInput },
47
+ });
48
+ const responseData = data?.data?.data || data?.data || data;
49
+ setResult(responseData);
50
+ } catch (err: any) {
51
+ setError(err?.response?.data?.errors?.[0]?.message || err.message || t('Execution failed'));
52
+ } finally {
53
+ setRunning(false);
54
+ }
55
+ };
56
+
57
+ return (
58
+ <Modal
59
+ open
60
+ title={`${t('Test Skill')}: ${skill.title}`}
61
+ onCancel={onClose}
62
+ footer={[
63
+ <Button key="close" onClick={onClose}>
64
+ {t('Close')}
65
+ </Button>,
66
+ <Button key="run" type="primary" onClick={handleRun} loading={running}>
67
+ {t('Run')}
68
+ </Button>,
69
+ ]}
70
+ width={640}
71
+ destroyOnClose
72
+ >
73
+ <Space direction="vertical" style={{ width: '100%' }} size="middle">
74
+ <div>
75
+ <Typography.Text strong>{t('Input (JSON)')}</Typography.Text>
76
+ <TextArea
77
+ rows={6}
78
+ value={input}
79
+ onChange={(e) => setInput(e.target.value)}
80
+ style={{ fontFamily: 'monospace', fontSize: 13, marginTop: 4 }}
81
+ />
82
+ </div>
83
+
84
+ {running && <Spin tip={t('Running skill on worker...')} />}
85
+
86
+ {error && <Alert type="error" message={error} showIcon />}
87
+
88
+ {result && (
89
+ <>
90
+ <Alert
91
+ type={result.status === 'succeeded' ? 'success' : 'error'}
92
+ message={result.status === 'succeeded' ? t('Succeeded') : t('Failed')}
93
+ description={`Duration: ${result.durationMs || 0}ms`}
94
+ showIcon
95
+ />
96
+
97
+ {result.stdout && (
98
+ <div>
99
+ <Typography.Text strong>stdout:</Typography.Text>
100
+ <pre style={{ background: '#f5f5f5', padding: 8, maxHeight: 200, overflow: 'auto', fontSize: 12 }}>
101
+ {result.stdout}
102
+ </pre>
103
+ </div>
104
+ )}
105
+
106
+ {result.stderr && (
107
+ <div>
108
+ <Typography.Text strong type="danger">
109
+ stderr:
110
+ </Typography.Text>
111
+ <pre style={{ background: '#fff2f0', padding: 8, maxHeight: 200, overflow: 'auto', fontSize: 12 }}>
112
+ {result.stderr}
113
+ </pre>
114
+ </div>
115
+ )}
116
+
117
+ {result.files?.length > 0 && (
118
+ <div>
119
+ <Typography.Text strong>{t('Output Files')}:</Typography.Text>
120
+ <div style={{ marginTop: 8 }}>
121
+ <FileLinkList
122
+ files={result.files.map((f: any) => ({
123
+ name: f.name,
124
+ url: f.downloadUrl,
125
+ }))}
126
+ />
127
+ </div>
128
+ </div>
129
+ )}
130
+ </>
131
+ )}
132
+ </Space>
133
+ </Modal>
134
+ );
135
+ };
@@ -0,0 +1,13 @@
1
+ import { useApp } from '@nocobase/client-v2';
2
+ import { useCallback } from 'react';
3
+
4
+ export const namespace = 'plugin-agent-orchestrator';
5
+
6
+ export function useT() {
7
+ const app = useApp();
8
+ return useCallback(
9
+ (str: string, options?: any): string =>
10
+ app.i18n.t(str, { ns: [namespace, 'client'], ...options }) as unknown as string,
11
+ [app.i18n],
12
+ );
13
+ }
@@ -0,0 +1,52 @@
1
+ export type InteractionSchema = {
2
+ type: 'form' | 'select' | 'confirm';
3
+ prompt: string;
4
+ options?: { label: string; value: string | number }[];
5
+ fields?: Record<string, { type?: string; title?: string; required?: boolean; enum?: any[] }>;
6
+ };
7
+
8
+ export type LoopTemplate = {
9
+ key: string;
10
+ title: string;
11
+ description: string;
12
+ schema: InteractionSchema;
13
+ };
14
+
15
+ export const LOOP_TEMPLATES: LoopTemplate[] = [
16
+ {
17
+ key: 'confirm',
18
+ title: 'Confirm before run',
19
+ description: 'Ask the user to approve or cancel the skill call.',
20
+ schema: {
21
+ type: 'confirm',
22
+ prompt: 'Review this skill call before execution.',
23
+ },
24
+ },
25
+ {
26
+ key: 'review-input',
27
+ title: 'Review editable input',
28
+ description: 'Show selected input fields so the user can edit them before running.',
29
+ schema: {
30
+ type: 'form',
31
+ prompt: 'Review and edit the skill input before execution.',
32
+ fields: {},
33
+ },
34
+ },
35
+ {
36
+ key: 'choose-option',
37
+ title: 'Choose one option',
38
+ description: 'Ask the user to choose one option before running.',
39
+ schema: {
40
+ type: 'select',
41
+ prompt: 'Choose how to run this skill.',
42
+ options: [
43
+ { label: 'Default', value: 'default' },
44
+ { label: 'Careful', value: 'careful' },
45
+ ],
46
+ },
47
+ },
48
+ ];
49
+
50
+ export function getLoopTemplate(key?: string) {
51
+ return LOOP_TEMPLATES.find((template) => template.key === key) || LOOP_TEMPLATES[0];
52
+ }
@@ -0,0 +1,41 @@
1
+ export function parseJsonText<T = any>(value: any, fallback: T): T {
2
+ if (value === undefined || value === null || value === '') return fallback;
3
+ if (typeof value !== 'string') return value;
4
+
5
+ const trimmed = value.trim();
6
+ const fenced = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
7
+ const json = fenced ? fenced[1].trim() : trimmed;
8
+
9
+ try {
10
+ let parsed = JSON.parse(json);
11
+ if (typeof parsed === 'string') {
12
+ const innerTrimmed = parsed.trim();
13
+ const innerFenced = innerTrimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
14
+ const innerJson = innerFenced ? innerFenced[1].trim() : innerTrimmed;
15
+ try {
16
+ parsed = JSON.parse(innerJson);
17
+ } catch {
18
+ // keep the string form if the inner payload is not JSON
19
+ }
20
+ }
21
+ return parsed;
22
+ } catch {
23
+ return fallback;
24
+ }
25
+ }
26
+
27
+ export function formatJsonText(value: any, fallback: any = null): string {
28
+ const parsed = parseJsonText(value, undefined);
29
+ const normalized =
30
+ parsed === undefined ? (value === undefined || value === null || value === '' ? fallback : value) : parsed;
31
+ if (normalized === undefined || normalized === null) return '';
32
+ if (typeof normalized === 'string') return normalized;
33
+ return JSON.stringify(normalized, null, 2);
34
+ }
35
+
36
+ export function stringifyJsonText(value: any, fallback: any = null): string {
37
+ const parsed = parseJsonText(value, undefined);
38
+ const normalized =
39
+ parsed === undefined ? (value === undefined || value === null || value === '' ? fallback : value) : parsed;
40
+ return `\`\`\`json\n${JSON.stringify(normalized, null, 2)}\n\`\`\``;
41
+ }
@@ -0,0 +1,41 @@
1
+ export function parseJsonText<T = any>(value: any, fallback: T): T {
2
+ if (value === undefined || value === null || value === '') return fallback;
3
+ if (typeof value !== 'string') return value;
4
+
5
+ const trimmed = value.trim();
6
+ const fenced = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
7
+ const json = fenced ? fenced[1].trim() : trimmed;
8
+
9
+ try {
10
+ let parsed = JSON.parse(json);
11
+ if (typeof parsed === 'string') {
12
+ const innerTrimmed = parsed.trim();
13
+ const innerFenced = innerTrimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
14
+ const innerJson = innerFenced ? innerFenced[1].trim() : innerTrimmed;
15
+ try {
16
+ parsed = JSON.parse(innerJson);
17
+ } catch {
18
+ // keep the string form if the inner payload is not JSON
19
+ }
20
+ }
21
+ return parsed;
22
+ } catch {
23
+ return fallback;
24
+ }
25
+ }
26
+
27
+ export function formatJsonText(value: any, fallback: any = null): string {
28
+ const parsed = parseJsonText(value, undefined);
29
+ const normalized =
30
+ parsed === undefined ? (value === undefined || value === null || value === '' ? fallback : value) : parsed;
31
+ if (normalized === undefined || normalized === null) return '';
32
+ if (typeof normalized === 'string') return normalized;
33
+ return JSON.stringify(normalized, null, 2);
34
+ }
35
+
36
+ export function stringifyJsonText(value: any, fallback: any = null): string {
37
+ const parsed = parseJsonText(value, undefined);
38
+ const normalized =
39
+ parsed === undefined ? (value === undefined || value === null || value === '' ? fallback : value) : parsed;
40
+ return `\`\`\`json\n${JSON.stringify(normalized, null, 2)}\n\`\`\``;
41
+ }
@@ -1,6 +1,13 @@
1
1
  {
2
2
  "Agent Orchestrator": "Agent Orchestrator",
3
3
  "Orchestration Rules": "Orchestration Rules",
4
+ "Execution Tracing": "Execution Tracing",
5
+ "Agent Runs": "Agent Runs",
6
+ "Harness Profiles": "Harness Profiles",
7
+ "Skill Hub Definitions": "Skill Hub Definitions",
8
+ "Execution History": "Execution History",
9
+ "Skill Review Settings": "Skill Review Settings",
10
+ "Metrics": "Metrics",
4
11
  "Swarm Tracing": "Swarm Tracing",
5
12
  "Leader (Orchestrator)": "Leader (Orchestrator)",
6
13
  "Sub-Agent": "Sub-Agent",
@@ -1,6 +1,13 @@
1
1
  {
2
2
  "Agent Orchestrator": "Điều phối Agent",
3
3
  "Orchestration Rules": "Quy tắc điều phối",
4
+ "Execution Tracing": "Truy vết thực thi",
5
+ "Agent Runs": "Lượt chạy Agent",
6
+ "Harness Profiles": "Hồ sơ Harness",
7
+ "Skill Hub Definitions": "Định nghĩa Skill Hub",
8
+ "Execution History": "Lịch sử thực thi",
9
+ "Skill Review Settings": "Cài đặt duyệt Skill",
10
+ "Metrics": "Số liệu",
4
11
  "Swarm Tracing": "Truy vết Swarm",
5
12
  "Leader (Orchestrator)": "Leader (Điều phối viên)",
6
13
  "Sub-Agent": "Agent con",
@@ -0,0 +1,27 @@
1
+ {
2
+ "Agent Orchestrator": "智能体编排",
3
+ "Orchestration Rules": "编排规则",
4
+ "Execution Tracing": "执行追踪",
5
+ "Agent Runs": "智能体运行",
6
+ "Harness Profiles": "Harness 配置",
7
+ "Skill Hub Definitions": "技能中心定义",
8
+ "Execution History": "执行历史",
9
+ "Skill Review Settings": "技能审核设置",
10
+ "Metrics": "指标",
11
+ "Swarm Tracing": "集群追踪",
12
+ "Leader (Orchestrator)": "主控(编排者)",
13
+ "Sub-Agent": "子智能体",
14
+ "Max Delegation Depth": "最大委派深度",
15
+ "Timeout (ms)": "超时时间(毫秒)",
16
+ "Enabled": "已启用",
17
+ "New Rule": "新建规则",
18
+ "Edit Orchestration Rule": "编辑编排规则",
19
+ "New Orchestration Rule": "新建编排规则",
20
+ "Rule created": "规则已创建",
21
+ "Rule updated": "规则已更新",
22
+ "Rule deleted": "规则已删除",
23
+ "Sub-Agent Conversation": "子智能体对话",
24
+ "Task Summary": "任务摘要",
25
+ "Parent Session": "父会话",
26
+ "No sub-agent executions yet": "暂无子智能体执行记录"
27
+ }
@@ -0,0 +1,147 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { AgentRegistryService } from '../services/AgentRegistryService';
3
+
4
+ function createService(configs: Array<{ leaderUsername: string; subAgentUsername: string }>) {
5
+ const find = vi.fn(async () => configs);
6
+ const service = new AgentRegistryService({
7
+ db: {
8
+ getRepository: vi.fn(() => ({ find })),
9
+ },
10
+ });
11
+ return { service, find };
12
+ }
13
+
14
+ function createServiceWithEmployees(employees: Record<string, any>) {
15
+ const findOne = vi.fn(async ({ filter }: { filter: { username: string } }) => employees[filter.username] ?? null);
16
+ const service = new AgentRegistryService({
17
+ db: {
18
+ getRepository: vi.fn(() => ({ findOne })),
19
+ },
20
+ });
21
+ return { service, findOne };
22
+ }
23
+
24
+ describe('AgentRegistryService', () => {
25
+ describe('isRegisteredDelegationTool', () => {
26
+ it('matches sanitized delegate tool names without parsing username delimiters', async () => {
27
+ const { service } = createService([
28
+ {
29
+ leaderUsername: 'lead.to_team',
30
+ subAgentUsername: 'qa_to_review',
31
+ },
32
+ ]);
33
+
34
+ await expect(service.isRegisteredDelegationTool('delegate_lead_to_team_to_qa_to_review')).resolves.toBe(true);
35
+ });
36
+
37
+ it('matches dispatch tools by generated sanitized name', async () => {
38
+ const { service } = createService([
39
+ {
40
+ leaderUsername: 'lead.to team',
41
+ subAgentUsername: 'researcher',
42
+ },
43
+ ]);
44
+
45
+ await expect(service.isRegisteredDelegationTool('dispatch_subagents_lead_to_team')).resolves.toBe(true);
46
+ });
47
+
48
+ it('keeps legacy alias valid only when one enabled config matches the sub-agent', async () => {
49
+ const single = createService([
50
+ {
51
+ leaderUsername: 'lead-a',
52
+ subAgentUsername: 'qa.to review',
53
+ },
54
+ ]);
55
+ await expect(single.service.isRegisteredDelegationTool('delegate_to_qa_to_review')).resolves.toBe(true);
56
+
57
+ const ambiguous = createService([
58
+ {
59
+ leaderUsername: 'lead-a',
60
+ subAgentUsername: 'qa.to review',
61
+ },
62
+ {
63
+ leaderUsername: 'lead-b',
64
+ subAgentUsername: 'qa.to review',
65
+ },
66
+ ]);
67
+ await expect(ambiguous.service.isRegisteredDelegationTool('delegate_to_qa_to_review')).resolves.toBe(false);
68
+ });
69
+ });
70
+
71
+ describe('resolveModelSettings', () => {
72
+ it('reads the dedicated models[] array shape saved by the admin UI', async () => {
73
+ const { service } = createServiceWithEmployees({
74
+ sub: {
75
+ username: 'sub',
76
+ modelSettings: {
77
+ enabled: true,
78
+ llmService: undefined,
79
+ model: undefined,
80
+ models: [{ llmService: 'openai', model: 'gpt-4o' }],
81
+ },
82
+ },
83
+ });
84
+
85
+ await expect(service.resolveModelSettings('sub')).resolves.toEqual({
86
+ llmService: 'openai',
87
+ model: 'gpt-4o',
88
+ });
89
+ });
90
+
91
+ it('falls back to the flat legacy { llmService, model } shape', async () => {
92
+ const { service } = createServiceWithEmployees({
93
+ sub: {
94
+ username: 'sub',
95
+ modelSettings: { llmService: 'anthropic', model: 'claude-3-5' },
96
+ },
97
+ });
98
+
99
+ await expect(service.resolveModelSettings('sub')).resolves.toEqual({
100
+ llmService: 'anthropic',
101
+ model: 'claude-3-5',
102
+ });
103
+ });
104
+
105
+ it('inherits the leader model when the sub-agent has none configured', async () => {
106
+ const { service } = createServiceWithEmployees({
107
+ sub: { username: 'sub', modelSettings: { enabled: false, models: [] } },
108
+ lead: {
109
+ username: 'lead',
110
+ modelSettings: {
111
+ enabled: true,
112
+ models: [{ llmService: 'openai', model: 'gpt-4o-mini' }],
113
+ },
114
+ },
115
+ });
116
+
117
+ await expect(service.resolveModelSettings('sub', 'lead')).resolves.toEqual({
118
+ llmService: 'openai',
119
+ model: 'gpt-4o-mini',
120
+ });
121
+ });
122
+
123
+ it('prefers an explicit per-rule override over employee settings', async () => {
124
+ const { service } = createServiceWithEmployees({
125
+ sub: {
126
+ username: 'sub',
127
+ modelSettings: { enabled: true, models: [{ llmService: 'openai', model: 'gpt-4o' }] },
128
+ },
129
+ });
130
+
131
+ await expect(
132
+ service.resolveModelSettings('sub', undefined, { llmService: 'anthropic', model: 'claude-3-5' }),
133
+ ).resolves.toEqual({ llmService: 'anthropic', model: 'claude-3-5' });
134
+ });
135
+
136
+ it('ignores dedicated models when not enabled and returns undefined with no fallback', async () => {
137
+ const { service } = createServiceWithEmployees({
138
+ sub: {
139
+ username: 'sub',
140
+ modelSettings: { enabled: false, models: [{ llmService: 'openai', model: 'gpt-4o' }] },
141
+ },
142
+ });
143
+
144
+ await expect(service.resolveModelSettings('sub')).resolves.toBeUndefined();
145
+ });
146
+ });
147
+ });