@zhin.js/core 1.0.25 → 1.0.27

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 (202) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +84 -342
  3. package/lib/adapter.d.ts +17 -0
  4. package/lib/adapter.d.ts.map +1 -1
  5. package/lib/adapter.js +84 -2
  6. package/lib/adapter.js.map +1 -1
  7. package/lib/ai/agent.d.ts +126 -0
  8. package/lib/ai/agent.d.ts.map +1 -0
  9. package/lib/ai/agent.js +645 -0
  10. package/lib/ai/agent.js.map +1 -0
  11. package/lib/ai/context-manager.d.ts +213 -0
  12. package/lib/ai/context-manager.d.ts.map +1 -0
  13. package/lib/ai/context-manager.js +313 -0
  14. package/lib/ai/context-manager.js.map +1 -0
  15. package/lib/ai/conversation-memory.d.ts +181 -0
  16. package/lib/ai/conversation-memory.d.ts.map +1 -0
  17. package/lib/ai/conversation-memory.js +581 -0
  18. package/lib/ai/conversation-memory.js.map +1 -0
  19. package/lib/ai/follow-up.d.ts +131 -0
  20. package/lib/ai/follow-up.d.ts.map +1 -0
  21. package/lib/ai/follow-up.js +265 -0
  22. package/lib/ai/follow-up.js.map +1 -0
  23. package/lib/ai/index.d.ts +29 -0
  24. package/lib/ai/index.d.ts.map +1 -0
  25. package/lib/ai/index.js +34 -0
  26. package/lib/ai/index.js.map +1 -0
  27. package/lib/ai/init.d.ts +30 -0
  28. package/lib/ai/init.d.ts.map +1 -0
  29. package/lib/ai/init.js +424 -0
  30. package/lib/ai/init.js.map +1 -0
  31. package/lib/ai/output.d.ts +93 -0
  32. package/lib/ai/output.d.ts.map +1 -0
  33. package/lib/ai/output.js +176 -0
  34. package/lib/ai/output.js.map +1 -0
  35. package/lib/ai/providers/anthropic.d.ts +23 -0
  36. package/lib/ai/providers/anthropic.d.ts.map +1 -0
  37. package/lib/ai/providers/anthropic.js +322 -0
  38. package/lib/ai/providers/anthropic.js.map +1 -0
  39. package/lib/ai/providers/base.d.ts +43 -0
  40. package/lib/ai/providers/base.d.ts.map +1 -0
  41. package/lib/ai/providers/base.js +135 -0
  42. package/lib/ai/providers/base.js.map +1 -0
  43. package/lib/ai/providers/index.d.ts +12 -0
  44. package/lib/ai/providers/index.d.ts.map +1 -0
  45. package/lib/ai/providers/index.js +9 -0
  46. package/lib/ai/providers/index.js.map +1 -0
  47. package/lib/ai/providers/ollama.d.ts +25 -0
  48. package/lib/ai/providers/ollama.d.ts.map +1 -0
  49. package/lib/ai/providers/ollama.js +243 -0
  50. package/lib/ai/providers/ollama.js.map +1 -0
  51. package/lib/ai/providers/openai.d.ts +46 -0
  52. package/lib/ai/providers/openai.d.ts.map +1 -0
  53. package/lib/ai/providers/openai.js +132 -0
  54. package/lib/ai/providers/openai.js.map +1 -0
  55. package/lib/ai/rate-limiter.d.ts +38 -0
  56. package/lib/ai/rate-limiter.d.ts.map +1 -0
  57. package/lib/ai/rate-limiter.js +86 -0
  58. package/lib/ai/rate-limiter.js.map +1 -0
  59. package/lib/ai/service.d.ts +81 -0
  60. package/lib/ai/service.d.ts.map +1 -0
  61. package/lib/ai/service.js +274 -0
  62. package/lib/ai/service.js.map +1 -0
  63. package/lib/ai/session.d.ts +186 -0
  64. package/lib/ai/session.d.ts.map +1 -0
  65. package/lib/ai/session.js +443 -0
  66. package/lib/ai/session.js.map +1 -0
  67. package/lib/ai/tone-detector.d.ts +19 -0
  68. package/lib/ai/tone-detector.d.ts.map +1 -0
  69. package/lib/ai/tone-detector.js +72 -0
  70. package/lib/ai/tone-detector.js.map +1 -0
  71. package/lib/ai/tools.d.ts +45 -0
  72. package/lib/ai/tools.d.ts.map +1 -0
  73. package/lib/ai/tools.js +206 -0
  74. package/lib/ai/tools.js.map +1 -0
  75. package/lib/ai/types.d.ts +264 -0
  76. package/lib/ai/types.d.ts.map +1 -0
  77. package/lib/ai/types.js +6 -0
  78. package/lib/ai/types.js.map +1 -0
  79. package/lib/ai/user-profile.d.ts +56 -0
  80. package/lib/ai/user-profile.d.ts.map +1 -0
  81. package/lib/ai/user-profile.js +130 -0
  82. package/lib/ai/user-profile.js.map +1 -0
  83. package/lib/ai/zhin-agent.d.ts +165 -0
  84. package/lib/ai/zhin-agent.d.ts.map +1 -0
  85. package/lib/ai/zhin-agent.js +707 -0
  86. package/lib/ai/zhin-agent.js.map +1 -0
  87. package/lib/built/ai-trigger.d.ts.map +1 -1
  88. package/lib/built/ai-trigger.js +7 -3
  89. package/lib/built/ai-trigger.js.map +1 -1
  90. package/lib/built/command.d.ts +33 -17
  91. package/lib/built/command.d.ts.map +1 -1
  92. package/lib/built/command.js +71 -44
  93. package/lib/built/command.js.map +1 -1
  94. package/lib/built/component.d.ts +42 -15
  95. package/lib/built/component.d.ts.map +1 -1
  96. package/lib/built/component.js +84 -52
  97. package/lib/built/component.js.map +1 -1
  98. package/lib/built/config.d.ts +64 -5
  99. package/lib/built/config.d.ts.map +1 -1
  100. package/lib/built/config.js +129 -12
  101. package/lib/built/config.js.map +1 -1
  102. package/lib/built/cron.d.ts +41 -18
  103. package/lib/built/cron.d.ts.map +1 -1
  104. package/lib/built/cron.js +106 -63
  105. package/lib/built/cron.js.map +1 -1
  106. package/lib/built/database.d.ts +55 -6
  107. package/lib/built/database.d.ts.map +1 -1
  108. package/lib/built/database.js +93 -22
  109. package/lib/built/database.js.map +1 -1
  110. package/lib/built/dispatcher.d.ts +118 -0
  111. package/lib/built/dispatcher.d.ts.map +1 -0
  112. package/lib/built/dispatcher.js +196 -0
  113. package/lib/built/dispatcher.js.map +1 -0
  114. package/lib/built/permission.d.ts +45 -5
  115. package/lib/built/permission.d.ts.map +1 -1
  116. package/lib/built/permission.js +56 -11
  117. package/lib/built/permission.js.map +1 -1
  118. package/lib/built/skill.d.ts +117 -0
  119. package/lib/built/skill.d.ts.map +1 -0
  120. package/lib/built/skill.js +191 -0
  121. package/lib/built/skill.js.map +1 -0
  122. package/lib/built/tool.d.ts +71 -164
  123. package/lib/built/tool.d.ts.map +1 -1
  124. package/lib/built/tool.js +212 -297
  125. package/lib/built/tool.js.map +1 -1
  126. package/lib/feature.d.ts +75 -0
  127. package/lib/feature.d.ts.map +1 -0
  128. package/lib/feature.js +69 -0
  129. package/lib/feature.js.map +1 -0
  130. package/lib/index.d.ts +4 -0
  131. package/lib/index.d.ts.map +1 -1
  132. package/lib/index.js +7 -0
  133. package/lib/index.js.map +1 -1
  134. package/lib/plugin.d.ts +25 -17
  135. package/lib/plugin.d.ts.map +1 -1
  136. package/lib/plugin.js +180 -20
  137. package/lib/plugin.js.map +1 -1
  138. package/lib/types.d.ts +4 -9
  139. package/lib/types.d.ts.map +1 -1
  140. package/package.json +6 -6
  141. package/src/adapter.ts +101 -2
  142. package/src/ai/agent.ts +772 -0
  143. package/src/ai/context-manager.ts +440 -0
  144. package/src/ai/conversation-memory.ts +774 -0
  145. package/src/ai/follow-up.ts +357 -0
  146. package/src/ai/index.ts +128 -0
  147. package/src/ai/init.ts +502 -0
  148. package/src/ai/output.ts +261 -0
  149. package/src/ai/providers/anthropic.ts +375 -0
  150. package/src/ai/providers/base.ts +173 -0
  151. package/src/ai/providers/index.ts +13 -0
  152. package/src/ai/providers/ollama.ts +292 -0
  153. package/src/ai/providers/openai.ts +167 -0
  154. package/src/ai/rate-limiter.ts +129 -0
  155. package/src/ai/service.ts +319 -0
  156. package/src/ai/session.ts +544 -0
  157. package/src/ai/tone-detector.ts +89 -0
  158. package/src/ai/tools.ts +218 -0
  159. package/src/ai/types.ts +296 -0
  160. package/src/ai/user-profile.ts +181 -0
  161. package/src/ai/zhin-agent.ts +845 -0
  162. package/src/built/ai-trigger.ts +6 -3
  163. package/src/built/command.ts +75 -69
  164. package/src/built/component.ts +94 -76
  165. package/src/built/config.ts +288 -128
  166. package/src/built/cron.ts +117 -101
  167. package/src/built/database.ts +128 -33
  168. package/src/built/dispatcher.ts +332 -0
  169. package/src/built/permission.ts +146 -54
  170. package/src/built/skill.ts +280 -0
  171. package/src/built/tool.ts +245 -366
  172. package/src/feature.ts +113 -0
  173. package/src/index.ts +7 -0
  174. package/src/plugin.ts +198 -33
  175. package/src/types.ts +6 -10
  176. package/tests/adapter.test.ts +153 -1
  177. package/tests/ai/agent.test.ts +614 -0
  178. package/tests/ai/ai-trigger.test.ts +368 -0
  179. package/tests/ai/context-manager.test.ts +413 -0
  180. package/tests/ai/conversation-memory.test.ts +128 -0
  181. package/tests/ai/follow-up.test.ts +175 -0
  182. package/tests/ai/integration.test.ts +584 -0
  183. package/tests/ai/output.test.ts +128 -0
  184. package/tests/ai/providers.integration.test.ts +227 -0
  185. package/tests/ai/rate-limiter.test.ts +108 -0
  186. package/tests/ai/session.test.ts +375 -0
  187. package/tests/ai/setup.ts +308 -0
  188. package/tests/ai/tone-detector.test.ts +80 -0
  189. package/tests/ai/tool.test.ts +800 -0
  190. package/tests/ai/tools-builtin.test.ts +346 -0
  191. package/tests/ai/user-profile.test.ts +73 -0
  192. package/tests/ai/zhin-agent.test.ts +177 -0
  193. package/tests/config.test.ts +46 -0
  194. package/tests/cron.test.ts +94 -5
  195. package/tests/dispatcher.test.ts +146 -0
  196. package/tests/feature.test.ts +145 -0
  197. package/tests/features-builtin.test.ts +191 -0
  198. package/tests/plugin.test.ts +88 -14
  199. package/tests/skill-feature.test.ts +179 -0
  200. package/tests/tool-feature.test.ts +254 -0
  201. package/test/minimal-bot.ts +0 -31
  202. package/test/stress-test.ts +0 -123
@@ -106,8 +106,9 @@ export const DEFAULT_AI_TRIGGER_CONFIG: Required<AITriggerConfig> = {
106
106
  * 检查消息是否 @ 了机器人
107
107
  */
108
108
  export function isAtBot<T extends object>(message: Message<T>): boolean {
109
+ const botId = String(message.$bot);
109
110
  return message.$content.some(seg => {
110
- return seg.data?.qq === message.$bot || seg.data?.user_id === message.$bot;
111
+ return String(seg.data?.qq) === botId || String(seg.data?.user_id) === botId;
111
112
  });
112
113
  }
113
114
 
@@ -142,10 +143,12 @@ export function parseRichMediaContent(content: string): MessageElement[] {
142
143
  * 移除 @ 机器人的部分
143
144
  */
144
145
  export function removeAtBot<T extends object>(message: Message<T>): MessageElement[] {
146
+ const botId = String(message.$bot);
145
147
  return message.$content.filter(seg => {
146
148
  const { type, data } = seg;
147
- const userId = data?.user_id || data?.qq;
148
- return type !== "at" && userId !== message.$bot;
149
+ if (type !== "at") return true; // 非 at 段全部保留
150
+ const userId = String(data?.user_id || data?.qq);
151
+ return userId !== botId; // 只移除 @机器人 的段,保留 @其他人
149
152
  });
150
153
  }
151
154
 
@@ -1,10 +1,11 @@
1
1
  /**
2
- * Command Context
3
- * 管理所有插件注册的命令
2
+ * CommandFeature
3
+ * 管理所有插件注册的命令,继承自 Feature 抽象类
4
4
  */
5
+ import { Feature, FeatureJSON } from "../feature.js";
5
6
  import { MessageCommand } from "../command.js";
6
7
  import { Message } from "../message.js";
7
- import { Context, Plugin, getPlugin } from "../plugin.js";
8
+ import { Plugin, getPlugin } from "../plugin.js";
8
9
  import type { RegisteredAdapter, AdapterMessage } from "../types.js";
9
10
 
10
11
  /**
@@ -20,83 +21,88 @@ declare module "../plugin.js" {
20
21
  namespace Plugin {
21
22
  interface Extensions extends CommandContextExtensions {}
22
23
  interface Contexts {
23
- command: CommandService;
24
+ command: CommandFeature;
24
25
  }
25
26
  }
26
27
  }
27
28
 
28
29
  /**
29
- * 命令服务数据
30
+ * 命令服务 Feature
30
31
  */
31
- export interface CommandService {
32
- /** 命令列表(保持顺序) */
33
- readonly items: MessageCommand<RegisteredAdapter>[];
32
+ export class CommandFeature extends Feature<MessageCommand<RegisteredAdapter>> {
33
+ readonly name = 'command' as const;
34
+ readonly icon = 'Terminal';
35
+ readonly desc = '命令';
36
+
34
37
  /** 按 pattern 索引 */
35
- readonly byName: Map<string, MessageCommand<RegisteredAdapter>>;
36
- /** 添加命令 */
37
- add(command: MessageCommand<RegisteredAdapter>, pluginName: string): () => void;
38
- /** 移除命令 */
39
- remove(command: MessageCommand<RegisteredAdapter>): boolean;
40
- /** 按名称获取 */
41
- get(pattern: string): MessageCommand<RegisteredAdapter> | undefined;
42
- /** 处理消息 */
43
- handle(message: Message<AdapterMessage<RegisteredAdapter>>, plugin: Plugin): Promise<any>;
44
- }
38
+ readonly byName = new Map<string, MessageCommand<RegisteredAdapter>>();
45
39
 
46
- /**
47
- * 创建命令 Context
48
- */
49
- export function createCommandService(): Context<'command', CommandContextExtensions> {
50
- const items: MessageCommand<RegisteredAdapter>[] = [];
51
- const byName = new Map<string, MessageCommand<RegisteredAdapter>>();
52
- const pluginMap = new Map<MessageCommand<RegisteredAdapter>, string>();
53
-
54
- const value: CommandService = {
55
- items,
56
- byName,
57
-
58
- add(command, pluginName) {
59
- items.push(command);
60
- byName.set(command.pattern, command);
61
- pluginMap.set(command, pluginName);
62
- return () => value.remove(command);
63
- },
64
-
65
- remove(command) {
66
- const index = items.indexOf(command);
67
- if (index !== -1) {
68
- items.splice(index, 1);
69
- byName.delete(command.pattern);
70
- pluginMap.delete(command);
71
- return true;
72
- }
73
- return false;
74
- },
75
-
76
- get(pattern) {
77
- return byName.get(pattern);
78
- },
79
-
80
- async handle(message, plugin) {
81
- for (const command of items) {
82
- const result = await command.handle(message, plugin);
83
- if (result) return result;
84
- }
85
- return null;
40
+ /**
41
+ * 添加命令
42
+ */
43
+ add(command: MessageCommand<RegisteredAdapter>, pluginName: string): () => void {
44
+ this.byName.set(command.pattern, command);
45
+ return super.add(command, pluginName);
46
+ }
47
+
48
+ /**
49
+ * 移除命令
50
+ */
51
+ remove(command: MessageCommand<RegisteredAdapter>): boolean {
52
+ this.byName.delete(command.pattern);
53
+ return super.remove(command);
54
+ }
55
+
56
+ /**
57
+ * 按 pattern 获取命令
58
+ */
59
+ get(pattern: string): MessageCommand<RegisteredAdapter> | undefined {
60
+ return this.byName.get(pattern);
61
+ }
62
+
63
+ /**
64
+ * 处理消息,依次尝试匹配命令
65
+ */
66
+ async handle(message: Message<AdapterMessage<RegisteredAdapter>>, plugin: Plugin): Promise<any> {
67
+ for (const command of this.items) {
68
+ const result = await command.handle(message, plugin);
69
+ if (result) return result;
86
70
  }
87
- };
88
-
89
- return {
90
- name: 'command',
91
- description: '命令服务',
92
- value,
93
- extensions: {
71
+ return null;
72
+ }
73
+
74
+ /**
75
+ * 序列化为 JSON
76
+ */
77
+ toJSON(pluginName?: string): FeatureJSON {
78
+ const list = pluginName ? this.getByPlugin(pluginName) : this.items;
79
+ return {
80
+ name: this.name,
81
+ icon: this.icon,
82
+ desc: this.desc,
83
+ count: list.length,
84
+ items: list.map(c => ({
85
+ name: c.pattern,
86
+ desc: c.helpInfo?.desc,
87
+ usage: c.helpInfo?.usage,
88
+ examples: c.helpInfo?.examples,
89
+ })),
90
+ };
91
+ }
92
+
93
+ /**
94
+ * 提供给 Plugin.prototype 的扩展方法
95
+ */
96
+ get extensions() {
97
+ const feature = this;
98
+ return {
94
99
  addCommand<T extends RegisteredAdapter>(command: MessageCommand<T>) {
95
100
  const plugin = getPlugin();
96
- const dispose = value.add(command as MessageCommand<RegisteredAdapter>, plugin.name);
101
+ const dispose = feature.add(command as MessageCommand<RegisteredAdapter>, plugin.name);
102
+ plugin.recordFeatureContribution(feature.name, command.pattern);
97
103
  plugin.onDispose(dispose);
98
104
  return dispose;
99
- }
100
- }
101
- };
105
+ },
106
+ };
107
+ }
102
108
  }
@@ -1,10 +1,11 @@
1
1
  /**
2
- * Component Context
3
- * 管理所有插件注册的组件
2
+ * ComponentFeature
3
+ * 管理所有插件注册的组件,继承自 Feature 抽象类
4
4
  */
5
+ import { Feature, FeatureJSON } from "../feature.js";
5
6
  import { Component, renderComponents } from "../component.js";
6
7
  import { SendOptions, MaybePromise } from "../types.js";
7
- import { Context, Plugin, getPlugin } from "../plugin.js";
8
+ import { Plugin, getPlugin } from "../plugin.js";
8
9
 
9
10
  type Listener = (options: SendOptions) => MaybePromise<SendOptions>;
10
11
 
@@ -21,90 +22,107 @@ declare module "../plugin.js" {
21
22
  namespace Plugin {
22
23
  interface Extensions extends ComponentContextExtensions {}
23
24
  interface Contexts {
24
- component: ComponentService;
25
+ component: ComponentFeature;
25
26
  }
26
27
  }
27
28
  }
28
29
 
29
30
  /**
30
- * 组件服务数据
31
+ * 组件服务 Feature
31
32
  */
32
- export interface ComponentService {
33
+ export class ComponentFeature extends Feature<Component<any>> {
34
+ readonly name = 'component' as const;
35
+ readonly icon = 'Box';
36
+ readonly desc = '组件';
37
+
33
38
  /** 按名称索引 */
34
- readonly byName: Map<string, Component<any>>;
35
- /** 添加组件 */
36
- add(component: Component<any>, pluginName: string): () => void;
37
- /** 获取所有组件名称 */
38
- getAllNames(): string[];
39
- /** 移除组件 */
40
- remove(component: Component<any>): boolean;
41
- /** 按名称获取 */
42
- get(name: string): Component<any> | undefined;
43
- }
39
+ readonly byName = new Map<string, Component<any>>();
44
40
 
45
- /**
46
- * 创建组件 Context
47
- */
48
- export function createComponentService(): Context<'component', ComponentContextExtensions> {
49
- const byName = new Map<string, Component<any>>();
50
- const pluginMap = new Map<Component<any>, string>();
51
- let listener: Listener | undefined;
52
- let rootPlugin: Plugin | undefined;
53
-
54
- const value: ComponentService = {
55
- byName,
56
-
57
- add(component, pluginName) {
58
- byName.set(component.name, component);
59
- pluginMap.set(component, pluginName);
60
- return () => value.remove(component);
61
- },
62
- getAllNames() {
63
- return Array.from(byName.keys());
64
- },
65
- remove(component) {
66
- if (byName.has(component.name)) {
67
- byName.delete(component.name);
68
- pluginMap.delete(component);
69
- return true;
70
- }
71
- return false;
72
- },
73
-
74
- get(name) {
75
- return byName.get(name);
41
+ /** 内部状态:消息渲染监听器 & 宿主插件 */
42
+ #listener?: Listener;
43
+ #rootPlugin?: Plugin;
44
+
45
+ /**
46
+ * 添加组件
47
+ */
48
+ add(component: Component<any>, pluginName: string): () => void {
49
+ this.byName.set(component.name, component);
50
+ return super.add(component, pluginName);
51
+ }
52
+
53
+ /**
54
+ * 移除组件
55
+ */
56
+ remove(component: Component<any>): boolean {
57
+ this.byName.delete(component.name);
58
+ return super.remove(component);
59
+ }
60
+
61
+ /**
62
+ * 获取所有组件名称
63
+ */
64
+ getAllNames(): string[] {
65
+ return Array.from(this.byName.keys());
66
+ }
67
+
68
+ /**
69
+ * 按名称获取组件
70
+ */
71
+ get(name: string): Component<any> | undefined {
72
+ return this.byName.get(name);
73
+ }
74
+
75
+ /**
76
+ * 生命周期: 挂载时注册消息渲染监听器
77
+ */
78
+ mounted(plugin: Plugin): void {
79
+ this.#rootPlugin = plugin;
80
+ this.#listener = (options: SendOptions) => {
81
+ return renderComponents(this.byName, options);
82
+ };
83
+ plugin.root.on('before.sendMessage', this.#listener);
84
+ }
85
+
86
+ /**
87
+ * 生命周期: 销毁时移除监听器
88
+ */
89
+ dispose(): void {
90
+ if (this.#listener && this.#rootPlugin) {
91
+ this.#rootPlugin.root.off('before.sendMessage', this.#listener);
92
+ this.#listener = undefined;
76
93
  }
77
- };
78
-
79
- return {
80
- name: 'component',
81
- description: '组件服务',
82
-
83
- mounted(plugin: Plugin) {
84
- rootPlugin = plugin;
85
- // 创建消息渲染监听器
86
- listener = (options: SendOptions) => {
87
- return renderComponents(byName, options);
88
- };
89
- plugin.root.on('before.sendMessage', listener);
90
- return value;
91
- },
92
-
93
- dispose() {
94
- if (listener && rootPlugin) {
95
- rootPlugin.root.off('before.sendMessage', listener);
96
- listener = undefined;
97
- }
98
- },
99
-
100
- extensions: {
94
+ }
95
+
96
+ /**
97
+ * 序列化为 JSON
98
+ */
99
+ toJSON(pluginName?: string): FeatureJSON {
100
+ const list = pluginName ? this.getByPlugin(pluginName) : this.items;
101
+ return {
102
+ name: this.name,
103
+ icon: this.icon,
104
+ desc: this.desc,
105
+ count: list.length,
106
+ items: list.map(c => ({
107
+ name: c.name,
108
+ type: 'component',
109
+ })),
110
+ };
111
+ }
112
+
113
+ /**
114
+ * 提供给 Plugin.prototype 的扩展方法
115
+ */
116
+ get extensions() {
117
+ const feature = this;
118
+ return {
101
119
  addComponent<T extends Component<any>>(component: T) {
102
120
  const plugin = getPlugin();
103
- const dispose = value.add(component, plugin.name);
121
+ const dispose = feature.add(component, plugin.name);
122
+ plugin.recordFeatureContribution(feature.name, component.name);
104
123
  plugin.onDispose(dispose);
105
124
  return dispose;
106
- }
107
- }
108
- };
125
+ },
126
+ };
127
+ }
109
128
  }
110
-