@elizaos/cli 1.3.0 → 1.3.2

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 (148) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-2CUIHNPL.js → chunk-5GUS4CFO.js} +7 -2
  3. package/dist/{chunk-2ALAPQLV.js → chunk-E6XYTE3A.js} +296 -311
  4. package/dist/chunk-GXWWPFBO.js +39 -0
  5. package/dist/{chunk-I77ZRNYO.js → chunk-T2QDIXGU.js} +2 -2
  6. package/dist/commands/agent/actions/index.d.ts +5 -0
  7. package/dist/commands/agent/actions/index.js +2 -2
  8. package/dist/commands/agent/index.d.ts +2 -2
  9. package/dist/commands/agent/index.js +2 -2
  10. package/dist/commands/create/actions/index.js +3 -3
  11. package/dist/commands/create/index.js +4 -4
  12. package/dist/commands/shared/index.d.ts +11 -28
  13. package/dist/commands/shared/index.js +7 -3
  14. package/dist/index.js +541 -450
  15. package/dist/{registry-N626N4VG.js → registry-433S5F3Y.js} +2 -2
  16. package/dist/templates/plugin-quick-starter/.gitignore +66 -0
  17. package/dist/templates/plugin-quick-starter/.npmignore +5 -0
  18. package/dist/templates/plugin-quick-starter/package.json +11 -3
  19. package/dist/templates/plugin-quick-starter/src/__tests__/plugin.test.ts +499 -146
  20. package/dist/templates/plugin-quick-starter/src/__tests__/test-utils.ts +316 -115
  21. package/dist/templates/plugin-quick-starter/src/plugin.ts +7 -13
  22. package/dist/templates/plugin-starter/.gitignore +66 -0
  23. package/dist/templates/plugin-starter/.npmignore +5 -0
  24. package/dist/templates/plugin-starter/README.md +1 -1
  25. package/dist/templates/plugin-starter/package.json +11 -3
  26. package/dist/templates/plugin-starter/src/__tests__/integration.test.ts +13 -13
  27. package/dist/templates/plugin-starter/src/__tests__/plugin.test.ts +556 -129
  28. package/dist/templates/plugin-starter/src/__tests__/test-utils.ts +347 -115
  29. package/dist/templates/plugin-starter/src/plugin.ts +18 -22
  30. package/dist/templates/project-starter/.gitignore +57 -0
  31. package/dist/templates/project-starter/.npmignore +11 -0
  32. package/dist/templates/project-starter/README.md +1 -1
  33. package/dist/templates/project-starter/package.json +4 -4
  34. package/dist/templates/project-starter/src/__tests__/env.test.ts +3 -1
  35. package/dist/templates/project-starter/src/__tests__/file-structure.test.ts +3 -2
  36. package/dist/templates/project-starter/src/__tests__/integration.test.ts +1 -1
  37. package/dist/templates/project-starter/tsup.config.ts +2 -1
  38. package/dist/templates/project-tee-starter/.dockerignore +64 -14
  39. package/dist/templates/project-tee-starter/.gitignore +57 -0
  40. package/dist/templates/project-tee-starter/.npmignore +6 -0
  41. package/dist/templates/project-tee-starter/Dockerfile +9 -5
  42. package/dist/templates/project-tee-starter/GUIDE.md +103 -42
  43. package/dist/templates/project-tee-starter/README.md +39 -19
  44. package/dist/templates/project-tee-starter/__tests__/build-order.test.ts +62 -0
  45. package/dist/templates/project-tee-starter/__tests__/character.test.ts +19 -17
  46. package/dist/templates/project-tee-starter/__tests__/config.test.ts +10 -3
  47. package/dist/templates/project-tee-starter/__tests__/env.test.ts +2 -1
  48. package/dist/templates/project-tee-starter/__tests__/file-structure.test.ts +14 -3
  49. package/dist/templates/project-tee-starter/__tests__/frontend.test.ts +459 -0
  50. package/dist/templates/project-tee-starter/__tests__/plugin.test.ts +4 -2
  51. package/dist/templates/project-tee-starter/__tests__/routes.test.ts +15 -6
  52. package/dist/templates/project-tee-starter/__tests__/tee-validation.test.ts +295 -0
  53. package/dist/templates/project-tee-starter/__tests__/vite-config-utils.ts +39 -0
  54. package/dist/templates/project-tee-starter/docker-compose.yaml +5 -2
  55. package/dist/templates/{plugin-starter/dist → project-tee-starter}/index.html +3 -3
  56. package/dist/templates/project-tee-starter/package.json +34 -14
  57. package/dist/templates/project-tee-starter/postcss.config.js +3 -0
  58. package/dist/templates/project-tee-starter/scripts/install-test-deps.js +52 -0
  59. package/dist/templates/project-tee-starter/scripts/test-all.sh +82 -0
  60. package/dist/templates/project-tee-starter/src/frontend/index.css +106 -0
  61. package/dist/templates/project-tee-starter/src/frontend/index.html +20 -0
  62. package/dist/templates/project-tee-starter/src/frontend/index.tsx +370 -0
  63. package/dist/templates/project-tee-starter/src/frontend/panels.tsx +17 -0
  64. package/dist/templates/project-tee-starter/src/frontend/utils.ts +6 -0
  65. package/dist/templates/project-tee-starter/src/index.ts +6 -6
  66. package/dist/templates/project-tee-starter/src/plugin.ts +209 -59
  67. package/dist/templates/project-tee-starter/tailwind.config.js +62 -0
  68. package/dist/templates/project-tee-starter/tsconfig.build.json +2 -2
  69. package/dist/templates/project-tee-starter/tsconfig.json +8 -5
  70. package/dist/templates/project-tee-starter/tsup.config.ts +3 -2
  71. package/dist/templates/project-tee-starter/vite.config.ts +39 -0
  72. package/dist/url-utils-CKc_Ebt_.d.ts +35 -0
  73. package/dist/{utils-H66532NB.js → utils-DBLSDYBF.js} +2 -2
  74. package/package.json +12 -7
  75. package/templates/plugin-quick-starter/.gitignore +66 -0
  76. package/templates/plugin-quick-starter/.npmignore +5 -0
  77. package/templates/plugin-quick-starter/package.json +11 -3
  78. package/templates/plugin-quick-starter/src/__tests__/plugin.test.ts +499 -146
  79. package/templates/plugin-quick-starter/src/__tests__/test-utils.ts +316 -115
  80. package/templates/plugin-quick-starter/src/plugin.ts +7 -13
  81. package/templates/plugin-starter/.gitignore +66 -0
  82. package/templates/plugin-starter/.npmignore +5 -0
  83. package/templates/plugin-starter/README.md +1 -1
  84. package/templates/plugin-starter/package.json +11 -3
  85. package/templates/plugin-starter/src/__tests__/integration.test.ts +13 -13
  86. package/templates/plugin-starter/src/__tests__/plugin.test.ts +556 -129
  87. package/templates/plugin-starter/src/__tests__/test-utils.ts +347 -115
  88. package/templates/plugin-starter/src/plugin.ts +18 -22
  89. package/templates/project-starter/.gitignore +57 -0
  90. package/templates/project-starter/.npmignore +11 -0
  91. package/templates/project-starter/README.md +1 -1
  92. package/templates/project-starter/package.json +4 -4
  93. package/templates/project-starter/src/__tests__/env.test.ts +3 -1
  94. package/templates/project-starter/src/__tests__/file-structure.test.ts +3 -2
  95. package/templates/project-starter/src/__tests__/integration.test.ts +1 -1
  96. package/templates/project-starter/tsup.config.ts +2 -1
  97. package/templates/project-tee-starter/.dockerignore +64 -14
  98. package/templates/project-tee-starter/.gitignore +57 -0
  99. package/templates/project-tee-starter/.npmignore +6 -0
  100. package/templates/project-tee-starter/Dockerfile +9 -5
  101. package/templates/project-tee-starter/GUIDE.md +103 -42
  102. package/templates/project-tee-starter/README.md +39 -19
  103. package/templates/project-tee-starter/__tests__/build-order.test.ts +62 -0
  104. package/templates/project-tee-starter/__tests__/character.test.ts +19 -17
  105. package/templates/project-tee-starter/__tests__/config.test.ts +10 -3
  106. package/templates/project-tee-starter/__tests__/env.test.ts +2 -1
  107. package/templates/project-tee-starter/__tests__/file-structure.test.ts +14 -3
  108. package/templates/project-tee-starter/__tests__/frontend.test.ts +459 -0
  109. package/templates/project-tee-starter/__tests__/plugin.test.ts +4 -2
  110. package/templates/project-tee-starter/__tests__/routes.test.ts +15 -6
  111. package/templates/project-tee-starter/__tests__/tee-validation.test.ts +295 -0
  112. package/templates/project-tee-starter/__tests__/vite-config-utils.ts +39 -0
  113. package/templates/project-tee-starter/docker-compose.yaml +5 -2
  114. package/templates/{plugin-starter/dist → project-tee-starter}/index.html +3 -3
  115. package/templates/project-tee-starter/package.json +34 -14
  116. package/templates/project-tee-starter/postcss.config.js +3 -0
  117. package/templates/project-tee-starter/scripts/install-test-deps.js +52 -0
  118. package/templates/project-tee-starter/scripts/test-all.sh +82 -0
  119. package/templates/project-tee-starter/src/frontend/index.css +106 -0
  120. package/templates/project-tee-starter/src/frontend/index.html +20 -0
  121. package/templates/project-tee-starter/src/frontend/index.tsx +370 -0
  122. package/templates/project-tee-starter/src/frontend/panels.tsx +17 -0
  123. package/templates/project-tee-starter/src/frontend/utils.ts +6 -0
  124. package/templates/project-tee-starter/src/index.ts +6 -6
  125. package/templates/project-tee-starter/src/plugin.ts +209 -59
  126. package/templates/project-tee-starter/tailwind.config.js +62 -0
  127. package/templates/project-tee-starter/tsconfig.build.json +2 -2
  128. package/templates/project-tee-starter/tsconfig.json +8 -5
  129. package/templates/project-tee-starter/tsup.config.ts +3 -2
  130. package/templates/project-tee-starter/vite.config.ts +39 -0
  131. package/dist/chunk-4O6EZU37.js +0 -14
  132. package/dist/migration-guides/advanced-migration-guide.md +0 -459
  133. package/dist/migration-guides/completion-requirements.md +0 -379
  134. package/dist/migration-guides/integrated-migration-loop.md +0 -392
  135. package/dist/migration-guides/migration-guide.md +0 -712
  136. package/dist/migration-guides/prompt-and-generation-guide.md +0 -702
  137. package/dist/migration-guides/state-and-providers-guide.md +0 -544
  138. package/dist/migration-guides/testing-guide.md +0 -1021
  139. package/dist/templates/plugin-starter/dist/assets/index-CgkejLs_.css +0 -1
  140. package/dist/templates/plugin-starter/dist/assets/index-D1cHX53P.js +0 -49
  141. package/dist/templates/plugin-starter/dist/index.js +0 -387
  142. package/dist/templates/plugin-starter/dist/index.js.map +0 -1
  143. package/templates/plugin-starter/dist/.vite/manifest.json +0 -11
  144. package/templates/plugin-starter/dist/assets/index-CgkejLs_.css +0 -1
  145. package/templates/plugin-starter/dist/assets/index-D1cHX53P.js +0 -49
  146. package/templates/plugin-starter/dist/index.d.ts +0 -14
  147. package/templates/plugin-starter/dist/index.js +0 -387
  148. package/templates/plugin-starter/dist/index.js.map +0 -1
@@ -1,12 +1,28 @@
1
1
  import { describe, expect, it, spyOn, beforeEach, afterEach, beforeAll, afterAll } from 'bun:test';
2
2
  import { starterPlugin, StarterService } from '../index';
3
- import { ModelType, logger, type IAgentRuntime, type Service } from '@elizaos/core';
3
+ import {
4
+ type IAgentRuntime,
5
+ type Memory,
6
+ type State,
7
+ type Content,
8
+ type HandlerCallback,
9
+ ModelType,
10
+ logger,
11
+ EventType,
12
+ } from '@elizaos/core';
4
13
  import dotenv from 'dotenv';
14
+ import {
15
+ createMockRuntime,
16
+ createTestMemory,
17
+ createTestState,
18
+ createUUID,
19
+ testFixtures,
20
+ } from './test-utils';
5
21
 
6
22
  // Setup environment variables
7
23
  dotenv.config();
8
24
 
9
- // Need to spy on logger for documentation
25
+ // Need to spy on logger
10
26
  beforeAll(() => {
11
27
  spyOn(logger, 'info');
12
28
  spyOn(logger, 'error');
@@ -18,192 +34,529 @@ afterAll(() => {
18
34
  // No global restore needed in bun:test
19
35
  });
20
36
 
21
- // Create a real runtime for testing
22
- function createRealRuntime(): Partial<IAgentRuntime> {
23
- const services = new Map<string, Service>();
24
-
25
- // Create a real service instance if needed
26
- const createService = (serviceType: string): Service | null => {
27
- if (serviceType === StarterService.serviceType) {
28
- return new StarterService({
29
- character: {
30
- name: 'Test Character',
31
- system: 'You are a helpful assistant for testing.',
32
- },
33
- } as IAgentRuntime);
34
- }
35
- return null;
36
- };
37
-
38
- return {
39
- character: {
40
- name: 'Test Character',
41
- system: 'You are a helpful assistant for testing.',
42
- bio: 'A test character for unit testing',
43
- plugins: [],
44
- settings: {},
45
- },
46
- getSetting: (key: string) => null,
47
- db: {
48
- get: async (key: string) => null,
49
- set: async (key: string, value: unknown) => true,
50
- delete: async (key: string) => true,
51
- getKeys: async (pattern: string) => [],
52
- },
53
- getService: <T extends Service>(serviceType: string): T | null => {
54
- // Log the service request for debugging
55
- logger.debug(`Requesting service: ${serviceType}`);
56
-
57
- // Get from cache or create new
58
- if (!services.has(serviceType)) {
59
- logger.debug(`Creating new service: ${serviceType}`);
60
- const service = createService(serviceType);
61
- if (service) {
62
- services.set(serviceType, service);
63
- }
64
- }
65
-
66
- return (services.get(serviceType) as T) || null;
67
- },
68
- registerService: async (ServiceClass: typeof Service): Promise<void> => {
69
- logger.debug(`Registering service: ${ServiceClass.serviceType}`);
70
- const runtime = {
71
- character: {
72
- name: 'Test Character',
73
- system: 'You are a helpful assistant for testing.',
74
- bio: 'A test character for unit testing',
75
- },
76
- } as IAgentRuntime;
77
- const service = await ServiceClass.start(runtime);
78
- services.set(ServiceClass.serviceType, service);
79
- },
80
- };
81
- }
82
-
83
37
  describe('Plugin Configuration', () => {
84
38
  it('should have correct plugin metadata', () => {
85
- expect(starterPlugin.name).toBe('plugin-quick-starter');
86
- expect(starterPlugin.description).toBe('Quick backend-only plugin template for elizaOS');
87
- expect(starterPlugin.config).toBeDefined();
39
+ // Check that plugin has required metadata (values will change when template is used)
40
+ expect(starterPlugin.name).toBeDefined();
41
+ expect(starterPlugin.name).toMatch(/^[a-z0-9-]+$/); // Valid plugin name format
42
+ expect(starterPlugin.description).toBeDefined();
43
+ expect(starterPlugin.description.length).toBeGreaterThan(0);
44
+ expect(starterPlugin.actions).toBeDefined();
45
+ expect(starterPlugin.actions?.length).toBeGreaterThan(0);
46
+ expect(starterPlugin.providers).toBeDefined();
47
+ expect(starterPlugin.providers?.length).toBeGreaterThan(0);
48
+ expect(starterPlugin.services).toBeDefined();
49
+ expect(starterPlugin.services?.length).toBeGreaterThan(0);
50
+ expect(starterPlugin.models).toBeDefined();
51
+ expect(starterPlugin.models?.[ModelType.TEXT_SMALL]).toBeDefined();
52
+ expect(starterPlugin.models?.[ModelType.TEXT_LARGE]).toBeDefined();
53
+ expect(starterPlugin.routes).toBeDefined();
54
+ expect(starterPlugin.routes?.length).toBeGreaterThan(0);
55
+ expect(starterPlugin.events).toBeDefined();
56
+ });
57
+
58
+ it('should initialize with valid configuration', async () => {
59
+ const runtime = createMockRuntime();
60
+ const config = { EXAMPLE_PLUGIN_VARIABLE: 'test-value' };
61
+
62
+ if (starterPlugin.init) {
63
+ await starterPlugin.init(config, runtime);
64
+ expect(process.env.EXAMPLE_PLUGIN_VARIABLE).toBe('test-value');
65
+ }
66
+ });
67
+
68
+ it('should handle initialization without config', async () => {
69
+ const runtime = createMockRuntime();
70
+
71
+ if (starterPlugin.init) {
72
+ // Init should not throw even with empty config
73
+ await starterPlugin.init({}, runtime);
74
+ }
75
+ });
76
+
77
+ it('should throw error for invalid configuration', async () => {
78
+ const runtime = createMockRuntime();
79
+ const invalidConfig = { EXAMPLE_PLUGIN_VARIABLE: 123 }; // Should be string
80
+
81
+ if (starterPlugin.init) {
82
+ await expect(starterPlugin.init(invalidConfig as any, runtime)).rejects.toThrow(
83
+ 'Invalid plugin configuration'
84
+ );
85
+ }
86
+ });
87
+ });
88
+
89
+ describe('Hello World Action', () => {
90
+ let runtime: IAgentRuntime;
91
+ let helloWorldAction: any;
92
+
93
+ beforeEach(() => {
94
+ runtime = createMockRuntime();
95
+ helloWorldAction = starterPlugin.actions?.[0];
96
+ // Clear all spies before each test
97
+ (logger.info as any).calls = [];
98
+ (logger.error as any).calls = [];
99
+ (logger.debug as any).calls = [];
100
+ (logger.warn as any).calls = [];
101
+ });
102
+
103
+ it('should have hello world action', () => {
104
+ expect(helloWorldAction).toBeDefined();
105
+ expect(helloWorldAction?.name).toBe('HELLO_WORLD');
106
+ });
107
+
108
+ it('should always validate messages (current implementation)', async () => {
109
+ if (!helloWorldAction?.validate) {
110
+ throw new Error('Hello world action validate not found');
111
+ }
112
+
113
+ const validMessages = ['say hello', 'hello world', 'Please say HELLO', 'can you say hello?'];
114
+
115
+ // The current implementation always returns true
116
+ // This test documents the actual behavior
117
+ for (const text of validMessages) {
118
+ const message = createTestMemory({
119
+ content: { text, source: 'test' },
120
+ });
121
+ const isValid = await helloWorldAction.validate(runtime, message);
122
+ expect(isValid).toBe(true);
123
+ }
124
+ });
125
+
126
+ it('should properly validate hello messages', async () => {
127
+ if (!helloWorldAction?.validate) {
128
+ throw new Error('Hello world action validate not found');
129
+ }
130
+
131
+ // The current implementation always returns true
132
+ // Test that it accepts all messages
133
+ const helloMessages = ['hello', 'hi there', 'hey!', 'greetings', 'howdy partner'];
134
+ for (const text of helloMessages) {
135
+ const message = createTestMemory({
136
+ content: { text, source: 'test' },
137
+ });
138
+ const isValid = await helloWorldAction.validate(runtime, message);
139
+ expect(isValid).toBe(true);
140
+ }
141
+
142
+ // Should also accept non-hello messages since validate always returns true
143
+ const nonHelloMessages = ['goodbye', 'what is the weather', 'tell me a joke'];
144
+ for (const text of nonHelloMessages) {
145
+ const message = createTestMemory({
146
+ content: { text, source: 'test' },
147
+ });
148
+ const isValid = await helloWorldAction.validate(runtime, message);
149
+ expect(isValid).toBe(true);
150
+ }
151
+
152
+ // Test empty string - also returns true
153
+ const emptyMessage = createTestMemory({
154
+ content: { text: '', source: 'test' },
155
+ });
156
+ const isEmptyValid = await helloWorldAction.validate(runtime, emptyMessage);
157
+ expect(isEmptyValid).toBe(true);
88
158
  });
89
159
 
90
- it('should include the EXAMPLE_PLUGIN_VARIABLE in config', () => {
91
- expect(starterPlugin.config).toHaveProperty('EXAMPLE_PLUGIN_VARIABLE');
160
+ it('should validate even without text content', async () => {
161
+ if (!helloWorldAction?.validate) {
162
+ throw new Error('Hello world action validate not found');
163
+ }
164
+
165
+ const messageWithoutText = createTestMemory({
166
+ content: { source: 'test' } as Content,
167
+ });
168
+
169
+ const isValid = await helloWorldAction.validate(runtime, messageWithoutText);
170
+ // Always returns true since validate always returns true
171
+ expect(isValid).toBe(true);
92
172
  });
93
173
 
94
- it('should initialize properly', async () => {
95
- const originalEnv = process.env.EXAMPLE_PLUGIN_VARIABLE;
174
+ it('should handle hello world action with callback', async () => {
175
+ if (!helloWorldAction?.handler) {
176
+ throw new Error('Hello world action handler not found');
177
+ }
178
+
179
+ const message = createTestMemory({
180
+ content: { text: 'say hello', source: 'test' },
181
+ });
96
182
 
97
- try {
98
- process.env.EXAMPLE_PLUGIN_VARIABLE = 'test-value';
183
+ let callbackContent: any = null;
184
+ const callback: HandlerCallback = async (content: Content) => {
185
+ callbackContent = content;
186
+ return [];
187
+ };
188
+
189
+ const result = await helloWorldAction.handler(runtime, message, undefined, undefined, callback);
99
190
 
100
- // Initialize with config - using real runtime
101
- const runtime = createRealRuntime();
191
+ expect(result).toHaveProperty('text', 'Hello world!');
192
+ expect(result).toHaveProperty('success', true);
193
+ expect(result).toHaveProperty('data');
194
+ expect((result as any).data).toHaveProperty('actions', ['HELLO_WORLD']);
195
+ expect((result as any).data).toHaveProperty('source', 'test');
102
196
 
103
- if (starterPlugin.init) {
104
- await starterPlugin.init(
105
- { EXAMPLE_PLUGIN_VARIABLE: 'test-value' },
106
- runtime as IAgentRuntime
107
- );
108
- expect(true).toBe(true); // If we got here, init succeeded
109
- }
110
- } finally {
111
- process.env.EXAMPLE_PLUGIN_VARIABLE = originalEnv;
197
+ expect(callbackContent).toEqual({
198
+ text: 'Hello world!',
199
+ actions: ['HELLO_WORLD'],
200
+ source: 'test',
201
+ });
202
+ });
203
+
204
+ it('should handle errors gracefully', async () => {
205
+ if (!helloWorldAction?.handler) {
206
+ throw new Error('Hello world action handler not found');
112
207
  }
208
+
209
+ const message = createTestMemory({
210
+ content: { text: 'say hello', source: 'test' },
211
+ });
212
+
213
+ const errorCallback: HandlerCallback = async () => {
214
+ throw new Error('Callback error');
215
+ };
216
+
217
+ const result = await helloWorldAction.handler(
218
+ runtime,
219
+ message,
220
+ undefined,
221
+ undefined,
222
+ errorCallback
223
+ );
224
+
225
+ expect(result).toHaveProperty('success', false);
226
+ expect(result).toHaveProperty('error');
227
+ expect((result as any).error?.message).toBe('Callback error');
228
+ // Quick-starter plugin doesn't log errors
113
229
  });
114
230
 
115
- it('should have a valid config', () => {
116
- expect(starterPlugin.config).toBeDefined();
117
- if (starterPlugin.config) {
118
- // Check if the config has expected EXAMPLE_PLUGIN_VARIABLE property
119
- expect(Object.keys(starterPlugin.config)).toContain('EXAMPLE_PLUGIN_VARIABLE');
231
+ it('should handle missing callback gracefully', async () => {
232
+ if (!helloWorldAction?.handler) {
233
+ throw new Error('Hello world action handler not found');
120
234
  }
235
+
236
+ const message = createTestMemory({
237
+ content: { text: 'say hello', source: 'test' },
238
+ });
239
+
240
+ const result = await helloWorldAction.handler(
241
+ runtime,
242
+ message,
243
+ undefined,
244
+ undefined,
245
+ undefined
246
+ );
247
+
248
+ expect(result).toHaveProperty('text', 'Hello world!');
249
+ expect(result).toHaveProperty('success', true);
250
+ });
251
+
252
+ it('should handle state parameter correctly', async () => {
253
+ if (!helloWorldAction?.handler) {
254
+ throw new Error('Hello world action handler not found');
255
+ }
256
+
257
+ const message = createTestMemory({
258
+ content: { text: 'say hello', source: 'test' },
259
+ });
260
+
261
+ const state = createTestState({
262
+ values: { customValue: 'test-state' },
263
+ });
264
+
265
+ const result = await helloWorldAction.handler(runtime, message, state, undefined, undefined);
266
+
267
+ expect(result).toHaveProperty('success', true);
121
268
  });
122
269
  });
123
270
 
124
- describe('Plugin Models', () => {
125
- it('should have TEXT_SMALL model defined', () => {
126
- expect(starterPlugin.models?.[ModelType.TEXT_SMALL]).toBeDefined();
127
- if (starterPlugin.models) {
128
- expect(typeof starterPlugin.models[ModelType.TEXT_SMALL]).toBe('function');
271
+ describe('Hello World Provider', () => {
272
+ const provider = starterPlugin.providers?.[0];
273
+ let runtime: IAgentRuntime;
274
+
275
+ beforeEach(() => {
276
+ runtime = createMockRuntime();
277
+ });
278
+
279
+ it('should have hello world provider', () => {
280
+ expect(provider).toBeDefined();
281
+ expect(provider?.name).toBe('HELLO_WORLD_PROVIDER');
282
+ });
283
+
284
+ it('should provide hello world data', async () => {
285
+ if (!provider?.get) {
286
+ throw new Error('Hello world provider not found');
129
287
  }
288
+
289
+ const message = createTestMemory();
290
+ const state = createTestState();
291
+
292
+ const result = await provider.get(runtime, message, state);
293
+
294
+ expect(result).toHaveProperty('text', 'I am a provider');
295
+ expect(result).toHaveProperty('values');
296
+ expect(result.values).toEqual({});
297
+ expect(result).toHaveProperty('data');
298
+ expect(result.data).toEqual({});
130
299
  });
131
300
 
132
- it('should have TEXT_LARGE model defined', () => {
133
- expect(starterPlugin.models?.[ModelType.TEXT_LARGE]).toBeDefined();
134
- if (starterPlugin.models) {
135
- expect(typeof starterPlugin.models[ModelType.TEXT_LARGE]).toBe('function');
301
+ it('should provide consistent structure across calls', async () => {
302
+ if (!provider?.get) {
303
+ throw new Error('Hello world provider not found');
304
+ }
305
+
306
+ const message = createTestMemory();
307
+ const state = createTestState();
308
+
309
+ const result1 = await provider.get(runtime, message, state);
310
+ const result2 = await provider.get(runtime, message, state);
311
+
312
+ // Text and structure should be consistent
313
+ expect(result1.text).toBe(result2.text);
314
+ expect(result1.values).toEqual(result2.values);
315
+ expect(result1.data).toEqual(result2.data);
316
+ });
317
+ });
318
+
319
+ describe('Model Handlers', () => {
320
+ let runtime: IAgentRuntime;
321
+
322
+ beforeEach(() => {
323
+ runtime = createMockRuntime();
324
+ });
325
+
326
+ it('should handle TEXT_SMALL model', async () => {
327
+ const handler = starterPlugin.models?.[ModelType.TEXT_SMALL];
328
+ if (!handler) {
329
+ throw new Error('TEXT_SMALL model handler not found');
136
330
  }
331
+
332
+ const result = await handler(runtime, { prompt: 'Test prompt' });
333
+
334
+ expect(result).toContain('Never gonna give you up');
137
335
  });
138
336
 
139
- it('should return a response from TEXT_SMALL model', async () => {
140
- if (starterPlugin.models?.[ModelType.TEXT_SMALL]) {
141
- const runtime = createRealRuntime();
142
- const result = await starterPlugin.models[ModelType.TEXT_SMALL](runtime as IAgentRuntime, {
143
- prompt: 'test',
144
- });
337
+ it('should handle TEXT_LARGE model with custom parameters', async () => {
338
+ const handler = starterPlugin.models?.[ModelType.TEXT_LARGE];
339
+ if (!handler) {
340
+ throw new Error('TEXT_LARGE model handler not found');
341
+ }
145
342
 
146
- // Check that we get a non-empty string response
147
- expect(result).toBeTruthy();
148
- expect(typeof result).toBe('string');
149
- expect(result.length).toBeGreaterThan(10);
343
+ const result = await handler(runtime, {
344
+ prompt: 'Test prompt with custom settings',
345
+ maxTokens: 1000,
346
+ temperature: 0.5,
347
+ frequencyPenalty: 0.5,
348
+ presencePenalty: 0.5,
349
+ });
350
+
351
+ expect(result).toContain('Never gonna make you cry');
352
+ });
353
+
354
+ it('should handle empty prompt', async () => {
355
+ const handler = starterPlugin.models?.[ModelType.TEXT_SMALL];
356
+ if (!handler) {
357
+ throw new Error('TEXT_SMALL model handler not found');
150
358
  }
359
+
360
+ const result = await handler(runtime, { prompt: '' });
361
+
362
+ expect(typeof result).toBe('string');
363
+ expect(result.length).toBeGreaterThan(0);
364
+ });
365
+
366
+ it('should handle missing parameters', async () => {
367
+ const handler = starterPlugin.models?.[ModelType.TEXT_LARGE];
368
+ if (!handler) {
369
+ throw new Error('TEXT_LARGE model handler not found');
370
+ }
371
+
372
+ const result = await handler(runtime, { prompt: 'Test prompt' });
373
+
374
+ expect(typeof result).toBe('string');
375
+ expect(result.length).toBeGreaterThan(0);
151
376
  });
152
377
  });
153
378
 
154
- describe('StarterService', () => {
155
- it('should start the service', async () => {
156
- const runtime = createRealRuntime();
157
- const startResult = await StarterService.start(runtime as IAgentRuntime);
379
+ describe('API Routes', () => {
380
+ let runtime: IAgentRuntime;
381
+
382
+ beforeEach(() => {
383
+ runtime = createMockRuntime();
384
+ });
385
+
386
+ it('should handle status route', async () => {
387
+ const statusRoute = starterPlugin.routes?.[0];
388
+ if (!statusRoute?.handler) {
389
+ throw new Error('Status route handler not found');
390
+ }
391
+
392
+ const mockRes = {
393
+ json: (data: any) => {
394
+ mockRes._jsonData = data;
395
+ },
396
+ _jsonData: null as any,
397
+ };
398
+
399
+ await statusRoute.handler({}, mockRes, runtime);
400
+
401
+ expect(mockRes._jsonData).toBeDefined();
402
+ expect(mockRes._jsonData.status).toBe('ok');
403
+ expect(mockRes._jsonData.plugin).toBe('quick-starter');
404
+ expect(mockRes._jsonData.timestamp).toBeDefined();
405
+ });
406
+
407
+ it('should validate route configuration', () => {
408
+ const statusRoute = starterPlugin.routes?.[0];
409
+
410
+ expect(statusRoute).toBeDefined();
411
+ expect(statusRoute?.path).toBe('/api/status');
412
+ expect(statusRoute?.type).toBe('GET');
413
+ // Routes don't have a public property in the current implementation
414
+ expect(statusRoute?.handler).toBeDefined();
415
+ });
158
416
 
159
- expect(startResult).toBeDefined();
160
- expect(startResult.constructor.name).toBe('StarterService');
417
+ it('should handle request with query parameters', async () => {
418
+ const statusRoute = starterPlugin.routes?.[0];
419
+ if (!statusRoute?.handler) {
420
+ throw new Error('Status route handler not found');
421
+ }
422
+
423
+ const mockReq = {
424
+ query: {
425
+ verbose: 'true',
426
+ },
427
+ };
428
+
429
+ const mockRes = {
430
+ json: (data: any) => {
431
+ mockRes._jsonData = data;
432
+ },
433
+ _jsonData: null as any,
434
+ };
161
435
 
162
- // Test real functionality - check stop method is available
163
- expect(typeof startResult.stop).toBe('function');
436
+ await statusRoute.handler(mockReq, mockRes, runtime);
437
+
438
+ expect(mockRes._jsonData).toBeDefined();
439
+ expect(mockRes._jsonData.status).toBe('ok');
164
440
  });
441
+ });
165
442
 
166
- it('should stop the service', async () => {
167
- const runtime = createRealRuntime();
443
+ describe('Event Handlers', () => {
444
+ beforeEach(() => {
445
+ // Clear logger spy calls
446
+ (logger.debug as any).calls = [];
447
+ (logger.info as any).calls = [];
448
+ (logger.error as any).calls = [];
449
+ });
168
450
 
169
- // Start the service to get the actual instance
170
- const service = await StarterService.start(runtime as IAgentRuntime);
451
+ it('should log when MESSAGE_RECEIVED event is triggered', async () => {
452
+ const handler = starterPlugin.events?.[EventType.MESSAGE_RECEIVED]?.[0];
453
+ if (!handler) {
454
+ throw new Error('MESSAGE_RECEIVED event handler not found');
455
+ }
171
456
 
172
- // Spy on the real service's stop method
173
- const stopSpy = spyOn(service, 'stop');
457
+ const payload = testFixtures.messagePayload();
458
+ await handler(payload);
174
459
 
175
- // Mock getService to return our spied service
176
- const originalGetService = runtime.getService;
177
- runtime.getService = <T extends Service>(serviceType: string): T | null => {
178
- if (serviceType === StarterService.serviceType) {
179
- return service as T;
180
- }
181
- return null;
460
+ expect(logger.debug).toHaveBeenCalled();
461
+ });
462
+
463
+ it('should handle malformed event payload', async () => {
464
+ const handler = starterPlugin.events?.[EventType.MESSAGE_RECEIVED]?.[0];
465
+ if (!handler) {
466
+ throw new Error('MESSAGE_RECEIVED event handler not found');
467
+ }
468
+
469
+ const malformedPayload = {
470
+ // Missing required fields
471
+ runtime: createMockRuntime(),
182
472
  };
183
473
 
184
- // Call the static stop method
185
- await StarterService.stop(runtime as IAgentRuntime);
474
+ // Should not throw
475
+ // Handler doesn't actually use the payload, just logs
476
+ await handler(malformedPayload as any);
477
+ });
186
478
 
187
- // Verify the service's stop method was called
188
- expect(stopSpy).toHaveBeenCalled();
479
+ it('should handle event with empty message content', async () => {
480
+ const handler = starterPlugin.events?.[EventType.MESSAGE_RECEIVED]?.[0];
481
+ if (!handler) {
482
+ throw new Error('MESSAGE_RECEIVED event handler not found');
483
+ }
189
484
 
190
- // Restore original getService
191
- runtime.getService = originalGetService;
485
+ const payload = testFixtures.messagePayload({
486
+ content: {},
487
+ });
488
+
489
+ await handler(payload);
490
+ expect(logger.debug).toHaveBeenCalled();
192
491
  });
492
+ });
193
493
 
194
- it('should throw an error when stopping a non-existent service', async () => {
195
- const runtime = createRealRuntime();
196
- // Don't register a service, so getService will return null
494
+ describe('StarterService', () => {
495
+ let runtime: IAgentRuntime;
197
496
 
198
- // We'll patch the getService function to ensure it returns null
199
- const originalGetService = runtime.getService;
200
- runtime.getService = () => null;
497
+ beforeEach(() => {
498
+ runtime = createMockRuntime();
499
+ // Clear logger spy calls
500
+ (logger.info as any).calls = [];
501
+ (logger.error as any).calls = [];
502
+ });
201
503
 
202
- await expect(StarterService.stop(runtime as IAgentRuntime)).rejects.toThrow(
203
- 'Starter service not found'
204
- );
504
+ it('should start the service', async () => {
505
+ const service = await StarterService.start(runtime);
506
+ expect(service).toBeInstanceOf(StarterService);
507
+ expect(logger.info).toHaveBeenCalled();
508
+ });
509
+
510
+ it('should have correct service type', () => {
511
+ expect(StarterService.serviceType).toBe('starter');
512
+ });
513
+
514
+ it('should stop service correctly', async () => {
515
+ // Start service
516
+ const service = await StarterService.start(runtime);
517
+
518
+ // Create a new runtime with the service registered
519
+ const runtimeWithService = createMockRuntime({
520
+ getService: () => service as any,
521
+ });
522
+
523
+ // Stop service
524
+ await StarterService.stop(runtimeWithService);
525
+ expect(logger.info).toHaveBeenCalled();
526
+ });
205
527
 
206
- // Restore original getService function
207
- runtime.getService = originalGetService;
528
+ it('should throw error when stopping non-existent service', async () => {
529
+ const emptyRuntime = createMockRuntime({
530
+ getService: () => null,
531
+ });
532
+
533
+ await expect(StarterService.stop(emptyRuntime)).rejects.toThrow('Starter service not found');
534
+ });
535
+
536
+ it('should handle multiple start/stop cycles', async () => {
537
+ // First cycle
538
+ const service1 = await StarterService.start(runtime);
539
+ expect(service1).toBeInstanceOf(StarterService);
540
+
541
+ const runtimeWithService1 = createMockRuntime({
542
+ getService: () => service1 as any,
543
+ });
544
+ await StarterService.stop(runtimeWithService1);
545
+
546
+ // Second cycle
547
+ const service2 = await StarterService.start(runtime);
548
+ expect(service2).toBeInstanceOf(StarterService);
549
+
550
+ const runtimeWithService2 = createMockRuntime({
551
+ getService: () => service2 as any,
552
+ });
553
+ await StarterService.stop(runtimeWithService2);
554
+ });
555
+
556
+ it('should provide capability description', async () => {
557
+ const service = await StarterService.start(runtime);
558
+ expect(service.capabilityDescription).toBe(
559
+ 'This is a starter service which is attached to the agent through the starter plugin.'
560
+ );
208
561
  });
209
562
  });