plugin-agent-orchestrator 1.0.22 → 1.0.25
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 +8 -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 +60 -19
- package/src/client/AgentRunsTab.tsx +769 -764
- package/src/client/HarnessProfilesTab.tsx +257 -247
- package/src/client/RulesTab.tsx +787 -716
- package/src/client/TracingTab.tsx +9 -6
- package/src/client/plugin.tsx +34 -27
- package/src/client/skill-hub/components/ExecutionHistory.tsx +9 -8
- package/src/client/skill-hub/components/GitSkillImport.tsx +12 -5
- package/src/client/skill-hub/components/LoopSettings.tsx +2 -2
- package/src/client/skill-hub/components/SkillEditor.tsx +2 -2
- package/src/client/skill-hub/components/SkillManager.tsx +2 -2
- package/src/client/skill-hub/components/SkillMetrics.tsx +157 -124
- package/src/client/skill-hub/components/SkillTestPanel.tsx +14 -13
- package/src/client/skill-hub/index.tsx +58 -51
- package/src/client/skill-hub/locale.ts +1 -1
- package/src/client/skill-hub/tools/InteractionSchemasProvider.tsx +132 -99
- package/src/client/skill-hub/tools/registerSkillLoopCards.ts +71 -58
- package/src/client/tools/PlanApprovalCard.tsx +3 -2
- 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
|
@@ -1,51 +1,58 @@
|
|
|
1
|
-
import { Plugin } from '@nocobase/client';
|
|
2
|
-
import { SkillManager } from './components/SkillManager';
|
|
3
|
-
import { ExecutionHistory } from './components/ExecutionHistory';
|
|
4
|
-
import { SkillMetrics } from './components/SkillMetrics';
|
|
5
|
-
import { LoopSettings } from './components/LoopSettings';
|
|
6
|
-
import { InteractionSchemasProvider } from './tools/InteractionSchemasProvider';
|
|
7
|
-
import { registerSkillLoopCards } from './tools/registerSkillLoopCards';
|
|
8
|
-
|
|
9
|
-
export class PluginSkillHubClient extends Plugin {
|
|
10
|
-
async load() {
|
|
11
|
-
(this as any).app.use(InteractionSchemasProvider);
|
|
12
|
-
|
|
13
|
-
(this as any).app.pluginSettingsManager.add('skill-hub', {
|
|
14
|
-
title: (this as any).t('Skill Hub'),
|
|
15
|
-
icon: 'CodeOutlined',
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
(this as any).app.pluginSettingsManager.add('skill-hub.definitions', {
|
|
19
|
-
title: (this as any).t('Skill Definitions'),
|
|
20
|
-
Component: SkillManager,
|
|
21
|
-
aclSnippet: 'pm.skill-hub',
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
(this as any).app.pluginSettingsManager.add('skill-hub.loop-settings', {
|
|
25
|
-
title: (this as any).t('Skill Review Settings'),
|
|
26
|
-
Component: LoopSettings,
|
|
27
|
-
aclSnippet: 'pm.skill-hub',
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
(this as any).app.pluginSettingsManager.add('skill-hub.executions', {
|
|
31
|
-
title: (this as any).t('Execution History'),
|
|
32
|
-
Component: ExecutionHistory,
|
|
33
|
-
aclSnippet: 'pm.skill-hub',
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
(this as any).app.pluginSettingsManager.add('skill-hub.metrics', {
|
|
37
|
-
title: (this as any).t('Dashboard Metrics'),
|
|
38
|
-
Component: SkillMetrics,
|
|
39
|
-
aclSnippet: 'pm.skill-hub',
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
1
|
+
import { Plugin } from '@nocobase/client';
|
|
2
|
+
import { SkillManager } from './components/SkillManager';
|
|
3
|
+
import { ExecutionHistory } from './components/ExecutionHistory';
|
|
4
|
+
import { SkillMetrics } from './components/SkillMetrics';
|
|
5
|
+
import { LoopSettings } from './components/LoopSettings';
|
|
6
|
+
import { InteractionSchemasProvider } from './tools/InteractionSchemasProvider';
|
|
7
|
+
import { registerSkillLoopCards } from './tools/registerSkillLoopCards';
|
|
8
|
+
|
|
9
|
+
export class PluginSkillHubClient extends Plugin {
|
|
10
|
+
async load() {
|
|
11
|
+
(this as any).app.use(InteractionSchemasProvider);
|
|
12
|
+
|
|
13
|
+
(this as any).app.pluginSettingsManager.add('skill-hub', {
|
|
14
|
+
title: (this as any).t('Skill Hub'),
|
|
15
|
+
icon: 'CodeOutlined',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
(this as any).app.pluginSettingsManager.add('skill-hub.definitions', {
|
|
19
|
+
title: (this as any).t('Skill Definitions'),
|
|
20
|
+
Component: SkillManager,
|
|
21
|
+
aclSnippet: 'pm.skill-hub',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
(this as any).app.pluginSettingsManager.add('skill-hub.loop-settings', {
|
|
25
|
+
title: (this as any).t('Skill Review Settings'),
|
|
26
|
+
Component: LoopSettings,
|
|
27
|
+
aclSnippet: 'pm.skill-hub',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
(this as any).app.pluginSettingsManager.add('skill-hub.executions', {
|
|
31
|
+
title: (this as any).t('Execution History'),
|
|
32
|
+
Component: ExecutionHistory,
|
|
33
|
+
aclSnippet: 'pm.skill-hub',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
(this as any).app.pluginSettingsManager.add('skill-hub.metrics', {
|
|
37
|
+
title: (this as any).t('Dashboard Metrics'),
|
|
38
|
+
Component: SkillMetrics,
|
|
39
|
+
aclSnippet: 'pm.skill-hub',
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
(this as any).app.eventBus?.addEventListener?.('auth:tokenChanged', (event: Event) => {
|
|
43
|
+
const token = (event as CustomEvent<{ token?: string | null }>).detail?.token;
|
|
44
|
+
if (token) {
|
|
45
|
+
this.registerSkillUiCards();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await this.registerSkillUiCards();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private async registerSkillUiCards() {
|
|
53
|
+
await registerSkillLoopCards((this as any).app);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { SkillManager, ExecutionHistory, SkillMetrics, LoopSettings };
|
|
58
|
+
export default PluginSkillHubClient;
|
|
@@ -1,99 +1,132 @@
|
|
|
1
|
-
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import { parseJsonText } from '../utils/jsonFields';
|
|
4
|
-
import { InteractionSchema } from './loopTemplates';
|
|
5
|
-
|
|
6
|
-
const Ctx = createContext<Map<string, InteractionSchema>>(new Map());
|
|
7
|
-
|
|
8
|
-
export const useInteractionSchemas = () => useContext(Ctx);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
1
|
+
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
2
|
+
import { useApp } from '@nocobase/client-v2';
|
|
3
|
+
import { parseJsonText } from '../utils/jsonFields';
|
|
4
|
+
import { InteractionSchema } from './loopTemplates';
|
|
5
|
+
|
|
6
|
+
const Ctx = createContext<Map<string, InteractionSchema>>(new Map());
|
|
7
|
+
|
|
8
|
+
export const useInteractionSchemas = () => useContext(Ctx);
|
|
9
|
+
|
|
10
|
+
type ApiClientWithAuth = ReturnType<typeof useApp>['apiClient'] & {
|
|
11
|
+
auth?: {
|
|
12
|
+
getToken?: () => string | null | undefined;
|
|
13
|
+
token?: string | null;
|
|
14
|
+
};
|
|
15
|
+
app?: {
|
|
16
|
+
eventBus?: {
|
|
17
|
+
addEventListener?: (type: string, listener: EventListenerOrEventListenerObject) => void;
|
|
18
|
+
removeEventListener?: (type: string, listener: EventListenerOrEventListenerObject) => void;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const sanitize = (name: string) =>
|
|
24
|
+
name
|
|
25
|
+
.toLowerCase()
|
|
26
|
+
.replace(/[^a-z0-9_]/g, '_')
|
|
27
|
+
.replace(/_+/g, '_')
|
|
28
|
+
.replace(/^_|_$/g, '');
|
|
29
|
+
|
|
30
|
+
export const InteractionSchemasProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
|
|
31
|
+
const api = useApp().apiClient as ApiClientWithAuth;
|
|
32
|
+
const authToken = api.auth?.getToken?.() || api.auth?.token || '';
|
|
33
|
+
const [map, setMap] = useState<Map<string, InteractionSchema>>(new Map());
|
|
34
|
+
const [version, setVersion] = useState(0);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const refresh = () => setVersion((current) => current + 1);
|
|
38
|
+
const refreshAfterSignIn = (event: Event) => {
|
|
39
|
+
const token = (event as CustomEvent<{ token?: string | null }>).detail?.token;
|
|
40
|
+
if (token) {
|
|
41
|
+
refresh();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
window.addEventListener('skill-hub-loop-settings-changed', refresh);
|
|
45
|
+
api.app?.eventBus?.addEventListener?.('auth:tokenChanged', refreshAfterSignIn);
|
|
46
|
+
return () => {
|
|
47
|
+
window.removeEventListener('skill-hub-loop-settings-changed', refresh);
|
|
48
|
+
api.app?.eventBus?.removeEventListener?.('auth:tokenChanged', refreshAfterSignIn);
|
|
49
|
+
};
|
|
50
|
+
}, [api]);
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
let cancelled = false;
|
|
54
|
+
if (!authToken) {
|
|
55
|
+
return () => {
|
|
56
|
+
cancelled = true;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const extractList = (data: any) => {
|
|
60
|
+
const value = data?.data?.data ?? data?.data ?? data ?? [];
|
|
61
|
+
return Array.isArray(value) ? value : [];
|
|
62
|
+
};
|
|
63
|
+
const schemaFromLoopConfig = (config: any): InteractionSchema | null => {
|
|
64
|
+
const schema = parseJsonText<InteractionSchema | null>(config.schema, null);
|
|
65
|
+
if (schema) {
|
|
66
|
+
return config.prompt && !schema.prompt ? { ...schema, prompt: config.prompt } : schema;
|
|
67
|
+
}
|
|
68
|
+
if (config.prompt) {
|
|
69
|
+
return {
|
|
70
|
+
type: 'confirm',
|
|
71
|
+
prompt: config.prompt,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
Promise.all([
|
|
78
|
+
api.request({
|
|
79
|
+
url: 'skillDefinitions:list',
|
|
80
|
+
skipNotify: true,
|
|
81
|
+
params: {
|
|
82
|
+
filter: { enabled: true },
|
|
83
|
+
fields: ['id', 'name', 'autoCall', 'interactionSchema'],
|
|
84
|
+
pageSize: 500,
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
87
|
+
api
|
|
88
|
+
.request({
|
|
89
|
+
url: 'skillLoopConfigs:list',
|
|
90
|
+
skipNotify: true,
|
|
91
|
+
params: {
|
|
92
|
+
filter: { enabled: true },
|
|
93
|
+
fields: ['skillId', 'enabled', 'schema', 'prompt', 'templateKey'],
|
|
94
|
+
pageSize: 500,
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
.catch(() => ({ data: [] })),
|
|
98
|
+
])
|
|
99
|
+
.then(([skillsResponse, loopConfigsResponse]) => {
|
|
100
|
+
if (cancelled) return;
|
|
101
|
+
const next = new Map<string, InteractionSchema>();
|
|
102
|
+
const skills = extractList(skillsResponse.data);
|
|
103
|
+
const loopConfigs = extractList(loopConfigsResponse.data);
|
|
104
|
+
const skillsById = new Map(skills.map((skill: any) => [String(skill.id), skill]));
|
|
105
|
+
|
|
106
|
+
for (const s of skills) {
|
|
107
|
+
if (s.autoCall) continue;
|
|
108
|
+
const schema = parseJsonText<InteractionSchema | null>(s.interactionSchema, null);
|
|
109
|
+
if (!schema) continue;
|
|
110
|
+
next.set(sanitize(s.name), schema);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
for (const config of loopConfigs) {
|
|
114
|
+
const skill = skillsById.get(String(config.skillId));
|
|
115
|
+
if (!skill?.name) continue;
|
|
116
|
+
const schema = schemaFromLoopConfig(config);
|
|
117
|
+
if (!schema) continue;
|
|
118
|
+
next.set(sanitize(skill.name), schema);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
setMap(next);
|
|
122
|
+
})
|
|
123
|
+
.catch(() => {
|
|
124
|
+
// silently ignore — user may lack permission to list definitions
|
|
125
|
+
});
|
|
126
|
+
return () => {
|
|
127
|
+
cancelled = true;
|
|
128
|
+
};
|
|
129
|
+
}, [api, authToken, version]);
|
|
130
|
+
|
|
131
|
+
return <Ctx.Provider value={map}>{children}</Ctx.Provider>;
|
|
132
|
+
};
|
|
@@ -1,58 +1,71 @@
|
|
|
1
|
-
import { SkillHubCard } from './SkillHubCard';
|
|
2
|
-
import { parseJsonText } from '../utils/jsonFields';
|
|
3
|
-
|
|
4
|
-
const sanitize = (name: string) =>
|
|
5
|
-
name
|
|
6
|
-
.toLowerCase()
|
|
7
|
-
.replace(/[^a-z0-9_]/g, '_')
|
|
8
|
-
.replace(/_+/g, '_')
|
|
9
|
-
.replace(/^_|_$/g, '');
|
|
10
|
-
|
|
11
|
-
const extractList = (data: any) => {
|
|
12
|
-
const value = data?.data?.data ?? data?.data ?? data ?? [];
|
|
13
|
-
return Array.isArray(value) ? value : [];
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
1
|
+
import { SkillHubCard } from './SkillHubCard';
|
|
2
|
+
import { parseJsonText } from '../utils/jsonFields';
|
|
3
|
+
|
|
4
|
+
const sanitize = (name: string) =>
|
|
5
|
+
name
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.replace(/[^a-z0-9_]/g, '_')
|
|
8
|
+
.replace(/_+/g, '_')
|
|
9
|
+
.replace(/^_|_$/g, '');
|
|
10
|
+
|
|
11
|
+
const extractList = (data: any) => {
|
|
12
|
+
const value = data?.data?.data ?? data?.data ?? data ?? [];
|
|
13
|
+
return Array.isArray(value) ? value : [];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const getAuthToken = (app: any) => app?.apiClient?.auth?.getToken?.() || app?.apiClient?.auth?.token || '';
|
|
17
|
+
|
|
18
|
+
const registerSkillHubCard = (toolsManager: any, name: string) => {
|
|
19
|
+
try {
|
|
20
|
+
toolsManager.registerTools(name, { ui: { card: SkillHubCard } });
|
|
21
|
+
} catch (err: unknown) {
|
|
22
|
+
if (!(err instanceof Error) || !err.message.includes('override existing keys')) {
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export async function registerSkillLoopCards(app: any) {
|
|
29
|
+
const toolsManager = app.aiManager?.toolsManager;
|
|
30
|
+
if (!toolsManager) return;
|
|
31
|
+
registerSkillHubCard(toolsManager, 'skill_hub_execute');
|
|
32
|
+
if (!getAuthToken(app)) return;
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const skillsResponse = await app.apiClient.request({
|
|
36
|
+
url: 'skillDefinitions:list',
|
|
37
|
+
skipNotify: true,
|
|
38
|
+
params: {
|
|
39
|
+
filter: { enabled: true },
|
|
40
|
+
fields: ['id', 'name', 'autoCall', 'interactionSchema'],
|
|
41
|
+
pageSize: 500,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
let loopSkillIds = new Set<string>();
|
|
46
|
+
try {
|
|
47
|
+
const loopConfigsResponse = await app.apiClient.request({
|
|
48
|
+
url: 'skillLoopConfigs:list',
|
|
49
|
+
skipNotify: true,
|
|
50
|
+
params: {
|
|
51
|
+
filter: { enabled: true },
|
|
52
|
+
fields: ['skillId'],
|
|
53
|
+
pageSize: 500,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
loopSkillIds = new Set(extractList(loopConfigsResponse.data).map((config: any) => String(config.skillId)));
|
|
57
|
+
} catch {
|
|
58
|
+
// Older deployments may not have the collection before migration/sync.
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const skills = extractList(skillsResponse.data);
|
|
62
|
+
for (const skill of skills) {
|
|
63
|
+
const hasLoopConfig = loopSkillIds.has(String(skill.id));
|
|
64
|
+
const hasLegacySchema = !skill.autoCall && !!parseJsonText(skill.interactionSchema, null);
|
|
65
|
+
if (!hasLoopConfig && !hasLegacySchema) continue;
|
|
66
|
+
registerSkillHubCard(toolsManager, `skill_hub_${sanitize(skill.name)}`);
|
|
67
|
+
}
|
|
68
|
+
} catch {
|
|
69
|
+
// user without ACL or backend unavailable - skip silently
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Alert, Button, Card, Input, List, Space, Tag, Typography, message } from 'antd';
|
|
3
|
-
import { ToolsUIProperties
|
|
3
|
+
import { ToolsUIProperties } from '@nocobase/client';
|
|
4
|
+
import { useApp } from '@nocobase/client-v2';
|
|
4
5
|
|
|
5
6
|
const { Paragraph, Text } = Typography;
|
|
6
7
|
|
|
@@ -18,7 +19,7 @@ const summarizeArgsPlan = (plan: any[]) =>
|
|
|
18
19
|
}));
|
|
19
20
|
|
|
20
21
|
export const PlanApprovalCard: React.FC<ToolsUIProperties> = ({ toolCall, decisions }) => {
|
|
21
|
-
const api =
|
|
22
|
+
const api = useApp().apiClient;
|
|
22
23
|
const rawArgs = (toolCall.args as Record<string, any>) || {};
|
|
23
24
|
const runId = rawArgs.runId;
|
|
24
25
|
const [detail, setDetail] = React.useState<any>(null);
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
import { PlanApprovalCard } from './PlanApprovalCard';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { PlanApprovalCard } from './PlanApprovalCard';
|
|
2
|
+
|
|
3
|
+
const registerToolCard = (toolsManager: any, name: string) => {
|
|
4
|
+
try {
|
|
5
|
+
toolsManager.registerTools(name, { ui: { card: PlanApprovalCard } });
|
|
6
|
+
} catch (err: unknown) {
|
|
7
|
+
if (!(err instanceof Error) || !err.message.includes('override existing keys')) {
|
|
8
|
+
throw err;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export async function registerOrchestratorCards(app: any) {
|
|
14
|
+
const toolsManager = app.aiManager?.toolsManager;
|
|
15
|
+
if (!toolsManager) return;
|
|
16
|
+
registerToolCard(toolsManager, 'orchestrator_execute_plan');
|
|
17
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Select } from 'antd';
|
|
3
|
+
import { useAIEmployees } from './AIEmployeesContext';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Reusable Select component for AI Employees.
|
|
7
|
+
* Uses shared AIEmployeesContext instead of making its own API call.
|
|
8
|
+
*/
|
|
9
|
+
export const AIEmployeeSelect: React.FC<{
|
|
10
|
+
value?: string;
|
|
11
|
+
onChange?: (value: string) => void;
|
|
12
|
+
exclude?: string; // username to exclude (prevent self-reference)
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
}> = ({ value, onChange, exclude, placeholder = 'Select AI Employee...' }) => {
|
|
15
|
+
const { employees, loading } = useAIEmployees();
|
|
16
|
+
|
|
17
|
+
const options = React.useMemo(() => {
|
|
18
|
+
return employees
|
|
19
|
+
.filter((emp) => !exclude || emp.username !== exclude)
|
|
20
|
+
.map((emp) => ({
|
|
21
|
+
label: emp.nickname,
|
|
22
|
+
value: emp.username,
|
|
23
|
+
description: emp.about,
|
|
24
|
+
}));
|
|
25
|
+
}, [employees, exclude]);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Select
|
|
29
|
+
loading={loading}
|
|
30
|
+
options={options}
|
|
31
|
+
value={value}
|
|
32
|
+
onChange={onChange}
|
|
33
|
+
placeholder={placeholder}
|
|
34
|
+
showSearch
|
|
35
|
+
filterOption={(input, option) =>
|
|
36
|
+
(option?.label ?? '').toString().toLowerCase().includes(input.toLowerCase()) ||
|
|
37
|
+
(option?.value ?? '').toString().toLowerCase().includes(input.toLowerCase())
|
|
38
|
+
}
|
|
39
|
+
optionRender={(option) => (
|
|
40
|
+
<div>
|
|
41
|
+
<div style={{ fontWeight: 500 }}>{option.label}</div>
|
|
42
|
+
{option.data.description && <div style={{ fontSize: 12, color: '#888' }}>{option.data.description}</div>}
|
|
43
|
+
</div>
|
|
44
|
+
)}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
};
|