opc-agent 1.1.3 → 1.2.1

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 (156) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/CONTRIBUTING.md +75 -75
  3. package/README.md +429 -429
  4. package/README.zh-CN.md +415 -415
  5. package/dist/channels/web.js +256 -256
  6. package/dist/core/streaming.d.ts +56 -0
  7. package/dist/core/streaming.js +160 -0
  8. package/dist/deploy/hermes.js +22 -22
  9. package/dist/deploy/openclaw.js +31 -31
  10. package/dist/index.d.ts +4 -0
  11. package/dist/index.js +7 -1
  12. package/dist/providers/index.d.ts +1 -1
  13. package/dist/providers/index.js +13 -148
  14. package/dist/schema/oad.d.ts +3 -3
  15. package/dist/templates/code-reviewer.js +5 -5
  16. package/dist/templates/customer-service.js +2 -2
  17. package/dist/templates/data-analyst.js +5 -5
  18. package/dist/templates/knowledge-base.js +2 -2
  19. package/dist/templates/sales-assistant.js +4 -4
  20. package/dist/templates/teacher.js +6 -6
  21. package/dist/tools/gateway.d.ts +28 -0
  22. package/dist/tools/gateway.js +177 -0
  23. package/docs/.vitepress/config.ts +103 -103
  24. package/docs/api/cli.md +48 -48
  25. package/docs/api/oad-schema.md +64 -64
  26. package/docs/api/sdk.md +80 -80
  27. package/docs/guide/concepts.md +51 -51
  28. package/docs/guide/configuration.md +79 -79
  29. package/docs/guide/deployment.md +42 -42
  30. package/docs/guide/getting-started.md +44 -44
  31. package/docs/guide/templates.md +28 -28
  32. package/docs/guide/testing.md +84 -84
  33. package/docs/index.md +27 -27
  34. package/docs/zh/api/cli.md +54 -54
  35. package/docs/zh/api/oad-schema.md +87 -87
  36. package/docs/zh/api/sdk.md +102 -102
  37. package/docs/zh/guide/concepts.md +104 -104
  38. package/docs/zh/guide/configuration.md +135 -135
  39. package/docs/zh/guide/deployment.md +81 -81
  40. package/docs/zh/guide/getting-started.md +82 -82
  41. package/docs/zh/guide/templates.md +84 -84
  42. package/docs/zh/guide/testing.md +88 -88
  43. package/docs/zh/index.md +27 -27
  44. package/examples/customer-service-demo/README.md +90 -90
  45. package/examples/customer-service-demo/oad.yaml +107 -107
  46. package/package.json +1 -1
  47. package/src/analytics/index.ts +66 -66
  48. package/src/channels/discord.ts +192 -192
  49. package/src/channels/email.ts +177 -177
  50. package/src/channels/feishu.ts +236 -236
  51. package/src/channels/index.ts +15 -15
  52. package/src/channels/slack.ts +160 -160
  53. package/src/channels/telegram.ts +90 -90
  54. package/src/channels/voice.ts +106 -106
  55. package/src/channels/web.ts +17 -17
  56. package/src/channels/webhook.ts +199 -199
  57. package/src/channels/websocket.ts +87 -87
  58. package/src/channels/wechat.ts +149 -149
  59. package/src/core/a2a.ts +143 -143
  60. package/src/core/agent.ts +152 -152
  61. package/src/core/analytics-engine.ts +186 -186
  62. package/src/core/auth.ts +57 -57
  63. package/src/core/cache.ts +141 -141
  64. package/src/core/compose.ts +77 -77
  65. package/src/core/config.ts +14 -14
  66. package/src/core/errors.ts +148 -148
  67. package/src/core/hitl.ts +138 -138
  68. package/src/core/knowledge.ts +49 -4
  69. package/src/core/logger.ts +57 -57
  70. package/src/core/orchestrator.ts +215 -215
  71. package/src/core/performance.ts +187 -187
  72. package/src/core/rate-limiter.ts +128 -128
  73. package/src/core/room.ts +109 -109
  74. package/src/core/runtime.ts +152 -152
  75. package/src/core/sandbox.ts +101 -101
  76. package/src/core/security.ts +171 -171
  77. package/src/core/streaming.ts +195 -0
  78. package/src/core/types.ts +68 -68
  79. package/src/core/versioning.ts +106 -106
  80. package/src/core/watch.ts +178 -178
  81. package/src/core/workflow.ts +235 -235
  82. package/src/deploy/hermes.ts +156 -156
  83. package/src/deploy/openclaw.ts +200 -200
  84. package/src/dtv/data.ts +29 -29
  85. package/src/dtv/trust.ts +43 -43
  86. package/src/dtv/value.ts +47 -47
  87. package/src/i18n/index.ts +216 -216
  88. package/src/index.ts +6 -0
  89. package/src/marketplace/index.ts +223 -223
  90. package/src/memory/deepbrain.ts +108 -108
  91. package/src/memory/index.ts +34 -34
  92. package/src/plugins/index.ts +208 -208
  93. package/src/providers/index.ts +12 -3
  94. package/src/schema/oad.ts +155 -155
  95. package/src/skills/base.ts +16 -16
  96. package/src/skills/document.ts +100 -100
  97. package/src/skills/http.ts +35 -35
  98. package/src/skills/index.ts +27 -27
  99. package/src/skills/scheduler.ts +80 -80
  100. package/src/skills/webhook-trigger.ts +59 -59
  101. package/src/templates/code-reviewer.ts +34 -34
  102. package/src/templates/customer-service.ts +80 -80
  103. package/src/templates/data-analyst.ts +70 -70
  104. package/src/templates/executive-assistant.ts +71 -71
  105. package/src/templates/financial-advisor.ts +60 -60
  106. package/src/templates/knowledge-base.ts +31 -31
  107. package/src/templates/legal-assistant.ts +71 -71
  108. package/src/templates/sales-assistant.ts +79 -79
  109. package/src/templates/teacher.ts +79 -79
  110. package/src/testing/index.ts +181 -181
  111. package/src/tools/calculator.ts +73 -73
  112. package/src/tools/datetime.ts +149 -149
  113. package/src/tools/gateway.ts +220 -0
  114. package/src/tools/json-transform.ts +187 -187
  115. package/src/tools/mcp.ts +76 -76
  116. package/src/tools/text-analysis.ts +116 -116
  117. package/templates/Dockerfile +15 -15
  118. package/templates/code-reviewer/README.md +27 -27
  119. package/templates/code-reviewer/oad.yaml +41 -41
  120. package/templates/customer-service/README.md +22 -22
  121. package/templates/customer-service/oad.yaml +36 -36
  122. package/templates/docker-compose.yml +21 -21
  123. package/templates/ecommerce-assistant/README.md +45 -0
  124. package/templates/ecommerce-assistant/oad.yaml +47 -0
  125. package/templates/knowledge-base/README.md +28 -28
  126. package/templates/knowledge-base/oad.yaml +38 -38
  127. package/templates/sales-assistant/README.md +26 -26
  128. package/templates/sales-assistant/oad.yaml +43 -43
  129. package/templates/tech-support/README.md +43 -0
  130. package/templates/tech-support/oad.yaml +45 -0
  131. package/tests/a2a.test.ts +66 -66
  132. package/tests/agent.test.ts +72 -72
  133. package/tests/analytics.test.ts +50 -50
  134. package/tests/channel.test.ts +39 -39
  135. package/tests/e2e.test.ts +134 -134
  136. package/tests/errors.test.ts +83 -83
  137. package/tests/gateway.test.ts +71 -0
  138. package/tests/hitl.test.ts +71 -71
  139. package/tests/i18n.test.ts +41 -41
  140. package/tests/mcp.test.ts +54 -54
  141. package/tests/oad.test.ts +68 -68
  142. package/tests/performance.test.ts +115 -115
  143. package/tests/plugin.test.ts +74 -74
  144. package/tests/room.test.ts +106 -106
  145. package/tests/runtime.test.ts +42 -42
  146. package/tests/sandbox.test.ts +46 -46
  147. package/tests/security.test.ts +60 -60
  148. package/tests/streaming.test.ts +109 -0
  149. package/tests/templates.test.ts +77 -77
  150. package/tests/v070.test.ts +76 -76
  151. package/tests/versioning.test.ts +75 -75
  152. package/tests/voice.test.ts +61 -61
  153. package/tests/webhook.test.ts +29 -29
  154. package/tests/workflow.test.ts +143 -143
  155. package/tsconfig.json +19 -19
  156. package/vitest.config.ts +9 -9
@@ -0,0 +1,71 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { ToolGateway } from '../src/tools/gateway';
3
+ import type { ToolGatewayConfig } from '../src/tools/gateway';
4
+
5
+ const baseConfig: ToolGatewayConfig = {
6
+ enabled: true,
7
+ endpoint: 'https://gateway.example.com',
8
+ apiKey: 'test-key',
9
+ };
10
+
11
+ describe('ToolGateway', () => {
12
+ it('should load default tools when connect fails', async () => {
13
+ vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('network error')));
14
+ const gw = new ToolGateway(baseConfig);
15
+ await gw.connect();
16
+ expect(gw.isConnected).toBe(false);
17
+ expect(gw.toolCount).toBe(4);
18
+ expect(gw.listTools().map((t) => t.name)).toContain('gateway:web-search');
19
+ vi.unstubAllGlobals();
20
+ });
21
+
22
+ it('should filter tools by enabledTools config', async () => {
23
+ vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('fail')));
24
+ const gw = new ToolGateway({ ...baseConfig, enabledTools: ['web-search', 'tts'] });
25
+ await gw.connect();
26
+ expect(gw.toolCount).toBe(2);
27
+ vi.unstubAllGlobals();
28
+ });
29
+
30
+ it('should parse gateway discovery response', async () => {
31
+ vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
32
+ ok: true,
33
+ json: async () => ({
34
+ tools: [
35
+ { name: 'web-search', description: 'Search', inputSchema: {}, available: true },
36
+ ],
37
+ }),
38
+ }));
39
+ const gw = new ToolGateway(baseConfig);
40
+ await gw.connect();
41
+ expect(gw.isConnected).toBe(true);
42
+ expect(gw.toolCount).toBe(1);
43
+ vi.unstubAllGlobals();
44
+ });
45
+
46
+ it('should return MCPTool instances from getTools()', async () => {
47
+ vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('fail')));
48
+ const gw = new ToolGateway(baseConfig);
49
+ await gw.connect();
50
+ const tools = gw.getTools();
51
+ expect(tools.length).toBe(4);
52
+ expect(tools[0]).toHaveProperty('execute');
53
+ expect(tools[0]).toHaveProperty('name');
54
+ vi.unstubAllGlobals();
55
+ });
56
+
57
+ it('should handle invoke errors gracefully', async () => {
58
+ vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('timeout')));
59
+ const gw = new ToolGateway(baseConfig);
60
+ const result = await gw.invokeTool('web-search', { query: 'test' });
61
+ expect(result.isError).toBe(true);
62
+ expect(result.content).toContain('timeout');
63
+ vi.unstubAllGlobals();
64
+ });
65
+
66
+ it('should not discover tools when disabled', async () => {
67
+ const gw = new ToolGateway({ ...baseConfig, enabled: false });
68
+ await gw.connect();
69
+ expect(gw.toolCount).toBe(0);
70
+ });
71
+ });
@@ -1,71 +1,71 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import { HITLManager } from '../src/core/hitl';
3
-
4
- describe('HITLManager', () => {
5
- let hitl: HITLManager;
6
-
7
- beforeEach(() => {
8
- hitl = new HITLManager({
9
- requireApproval: ['delete', 'deploy'],
10
- defaultTimeoutMs: 500,
11
- defaultAction: 'deny',
12
- });
13
- });
14
-
15
- it('should check if action needs approval', () => {
16
- expect(hitl.needsApproval('delete')).toBe(true);
17
- expect(hitl.needsApproval('read')).toBe(false);
18
- });
19
-
20
- it('should approve via handler', async () => {
21
- hitl.setHandler(async (req) => ({
22
- requestId: req.id,
23
- decision: 'approve',
24
- respondedAt: Date.now(),
25
- timedOut: false,
26
- }));
27
-
28
- const response = await hitl.requestApproval('delete', 'Delete record #123');
29
- expect(response.decision).toBe('approve');
30
- expect(response.timedOut).toBe(false);
31
- });
32
-
33
- it('should deny via handler', async () => {
34
- hitl.setHandler(async (req) => ({
35
- requestId: req.id,
36
- decision: 'deny',
37
- respondedAt: Date.now(),
38
- timedOut: false,
39
- }));
40
-
41
- const response = await hitl.requestApproval('deploy', 'Deploy to production');
42
- expect(response.decision).toBe('deny');
43
- });
44
-
45
- it('should timeout with default action', async () => {
46
- // No handler, no manual response → timeout
47
- const response = await hitl.requestApproval('delete', 'Test timeout');
48
- expect(response.timedOut).toBe(true);
49
- expect(response.decision).toBe('deny'); // default action
50
- });
51
-
52
- it('should handle manual respond', async () => {
53
- const promise = hitl.requestApproval('delete', 'Manual test');
54
- const pending = hitl.getPending();
55
- expect(pending).toHaveLength(1);
56
-
57
- hitl.respond(pending[0].id, 'approve', 'admin');
58
- const response = await promise;
59
- expect(response.decision).toBe('approve');
60
- expect(response.respondedBy).toBe('admin');
61
- });
62
-
63
- it('should return false for unknown respond', () => {
64
- expect(hitl.respond('unknown-id', 'approve')).toBe(false);
65
- });
66
-
67
- it('should match wildcard approval', () => {
68
- const wildcard = new HITLManager({ requireApproval: ['*'] });
69
- expect(wildcard.needsApproval('anything')).toBe(true);
70
- });
71
- });
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { HITLManager } from '../src/core/hitl';
3
+
4
+ describe('HITLManager', () => {
5
+ let hitl: HITLManager;
6
+
7
+ beforeEach(() => {
8
+ hitl = new HITLManager({
9
+ requireApproval: ['delete', 'deploy'],
10
+ defaultTimeoutMs: 500,
11
+ defaultAction: 'deny',
12
+ });
13
+ });
14
+
15
+ it('should check if action needs approval', () => {
16
+ expect(hitl.needsApproval('delete')).toBe(true);
17
+ expect(hitl.needsApproval('read')).toBe(false);
18
+ });
19
+
20
+ it('should approve via handler', async () => {
21
+ hitl.setHandler(async (req) => ({
22
+ requestId: req.id,
23
+ decision: 'approve',
24
+ respondedAt: Date.now(),
25
+ timedOut: false,
26
+ }));
27
+
28
+ const response = await hitl.requestApproval('delete', 'Delete record #123');
29
+ expect(response.decision).toBe('approve');
30
+ expect(response.timedOut).toBe(false);
31
+ });
32
+
33
+ it('should deny via handler', async () => {
34
+ hitl.setHandler(async (req) => ({
35
+ requestId: req.id,
36
+ decision: 'deny',
37
+ respondedAt: Date.now(),
38
+ timedOut: false,
39
+ }));
40
+
41
+ const response = await hitl.requestApproval('deploy', 'Deploy to production');
42
+ expect(response.decision).toBe('deny');
43
+ });
44
+
45
+ it('should timeout with default action', async () => {
46
+ // No handler, no manual response → timeout
47
+ const response = await hitl.requestApproval('delete', 'Test timeout');
48
+ expect(response.timedOut).toBe(true);
49
+ expect(response.decision).toBe('deny'); // default action
50
+ });
51
+
52
+ it('should handle manual respond', async () => {
53
+ const promise = hitl.requestApproval('delete', 'Manual test');
54
+ const pending = hitl.getPending();
55
+ expect(pending).toHaveLength(1);
56
+
57
+ hitl.respond(pending[0].id, 'approve', 'admin');
58
+ const response = await promise;
59
+ expect(response.decision).toBe('approve');
60
+ expect(response.respondedBy).toBe('admin');
61
+ });
62
+
63
+ it('should return false for unknown respond', () => {
64
+ expect(hitl.respond('unknown-id', 'approve')).toBe(false);
65
+ });
66
+
67
+ it('should match wildcard approval', () => {
68
+ const wildcard = new HITLManager({ requireApproval: ['*'] });
69
+ expect(wildcard.needsApproval('anything')).toBe(true);
70
+ });
71
+ });
@@ -1,41 +1,41 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { t, setLocale, getLocale, detectLocale, addMessages } from '../src/i18n';
3
-
4
- describe('i18n', () => {
5
- it('should return English messages by default', () => {
6
- setLocale('en');
7
- expect(t('agent.greeting')).toBe('Hello! How can I help you?');
8
- });
9
-
10
- it('should return Chinese messages', () => {
11
- setLocale('zh-CN');
12
- expect(t('agent.greeting')).toBe('你好!有什么可以帮你的?');
13
- setLocale('en'); // reset
14
- });
15
-
16
- it('should interpolate params', () => {
17
- setLocale('en');
18
- expect(t('agent.started', { name: 'TestBot' })).toBe('Agent "TestBot" started successfully');
19
- });
20
-
21
- it('should fall back to key if not found', () => {
22
- expect(t('nonexistent.key')).toBe('nonexistent.key');
23
- });
24
-
25
- it('should get and set locale', () => {
26
- setLocale('zh-CN');
27
- expect(getLocale()).toBe('zh-CN');
28
- setLocale('en');
29
- expect(getLocale()).toBe('en');
30
- });
31
-
32
- it('should add custom messages', () => {
33
- addMessages('en', { 'custom.key': 'Custom value' });
34
- expect(t('custom.key')).toBe('Custom value');
35
- });
36
-
37
- it('should detect locale from environment', () => {
38
- const locale = detectLocale();
39
- expect(['en', 'zh-CN']).toContain(locale);
40
- });
41
- });
1
+ import { describe, it, expect } from 'vitest';
2
+ import { t, setLocale, getLocale, detectLocale, addMessages } from '../src/i18n';
3
+
4
+ describe('i18n', () => {
5
+ it('should return English messages by default', () => {
6
+ setLocale('en');
7
+ expect(t('agent.greeting')).toBe('Hello! How can I help you?');
8
+ });
9
+
10
+ it('should return Chinese messages', () => {
11
+ setLocale('zh-CN');
12
+ expect(t('agent.greeting')).toBe('你好!有什么可以帮你的?');
13
+ setLocale('en'); // reset
14
+ });
15
+
16
+ it('should interpolate params', () => {
17
+ setLocale('en');
18
+ expect(t('agent.started', { name: 'TestBot' })).toBe('Agent "TestBot" started successfully');
19
+ });
20
+
21
+ it('should fall back to key if not found', () => {
22
+ expect(t('nonexistent.key')).toBe('nonexistent.key');
23
+ });
24
+
25
+ it('should get and set locale', () => {
26
+ setLocale('zh-CN');
27
+ expect(getLocale()).toBe('zh-CN');
28
+ setLocale('en');
29
+ expect(getLocale()).toBe('en');
30
+ });
31
+
32
+ it('should add custom messages', () => {
33
+ addMessages('en', { 'custom.key': 'Custom value' });
34
+ expect(t('custom.key')).toBe('Custom value');
35
+ });
36
+
37
+ it('should detect locale from environment', () => {
38
+ const locale = detectLocale();
39
+ expect(['en', 'zh-CN']).toContain(locale);
40
+ });
41
+ });
package/tests/mcp.test.ts CHANGED
@@ -1,54 +1,54 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { MCPToolRegistry, createMCPTool } from '../src/tools/mcp';
3
-
4
- describe('MCP Tool System', () => {
5
- it('should register and list tools', () => {
6
- const registry = new MCPToolRegistry();
7
- const tool = createMCPTool('calculator', 'Basic calculator', {
8
- type: 'object',
9
- properties: { expression: { type: 'string' } },
10
- }, async (input) => ({ content: `Result: ${input.expression}` }));
11
-
12
- registry.register(tool);
13
- expect(registry.has('calculator')).toBe(true);
14
- expect(registry.list().length).toBe(1);
15
- expect(registry.list()[0].name).toBe('calculator');
16
- });
17
-
18
- it('should execute a tool', async () => {
19
- const registry = new MCPToolRegistry();
20
- registry.register(createMCPTool('echo', 'Echo tool', {}, async (input) => ({
21
- content: `Echo: ${input.text}`,
22
- })));
23
-
24
- const result = await registry.execute('echo', { text: 'hello' });
25
- expect(result.content).toBe('Echo: hello');
26
- expect(result.isError).toBeUndefined();
27
- });
28
-
29
- it('should return error for missing tool', async () => {
30
- const registry = new MCPToolRegistry();
31
- const result = await registry.execute('nonexistent', {});
32
- expect(result.isError).toBe(true);
33
- expect(result.content).toContain('not found');
34
- });
35
-
36
- it('should handle tool execution errors', async () => {
37
- const registry = new MCPToolRegistry();
38
- registry.register(createMCPTool('failing', 'Fails', {}, async () => {
39
- throw new Error('boom');
40
- }));
41
-
42
- const result = await registry.execute('failing', {});
43
- expect(result.isError).toBe(true);
44
- expect(result.content).toContain('boom');
45
- });
46
-
47
- it('should unregister tools', () => {
48
- const registry = new MCPToolRegistry();
49
- registry.register(createMCPTool('temp', 'Temp', {}, async () => ({ content: '' })));
50
- expect(registry.has('temp')).toBe(true);
51
- registry.unregister('temp');
52
- expect(registry.has('temp')).toBe(false);
53
- });
54
- });
1
+ import { describe, it, expect } from 'vitest';
2
+ import { MCPToolRegistry, createMCPTool } from '../src/tools/mcp';
3
+
4
+ describe('MCP Tool System', () => {
5
+ it('should register and list tools', () => {
6
+ const registry = new MCPToolRegistry();
7
+ const tool = createMCPTool('calculator', 'Basic calculator', {
8
+ type: 'object',
9
+ properties: { expression: { type: 'string' } },
10
+ }, async (input) => ({ content: `Result: ${input.expression}` }));
11
+
12
+ registry.register(tool);
13
+ expect(registry.has('calculator')).toBe(true);
14
+ expect(registry.list().length).toBe(1);
15
+ expect(registry.list()[0].name).toBe('calculator');
16
+ });
17
+
18
+ it('should execute a tool', async () => {
19
+ const registry = new MCPToolRegistry();
20
+ registry.register(createMCPTool('echo', 'Echo tool', {}, async (input) => ({
21
+ content: `Echo: ${input.text}`,
22
+ })));
23
+
24
+ const result = await registry.execute('echo', { text: 'hello' });
25
+ expect(result.content).toBe('Echo: hello');
26
+ expect(result.isError).toBeUndefined();
27
+ });
28
+
29
+ it('should return error for missing tool', async () => {
30
+ const registry = new MCPToolRegistry();
31
+ const result = await registry.execute('nonexistent', {});
32
+ expect(result.isError).toBe(true);
33
+ expect(result.content).toContain('not found');
34
+ });
35
+
36
+ it('should handle tool execution errors', async () => {
37
+ const registry = new MCPToolRegistry();
38
+ registry.register(createMCPTool('failing', 'Fails', {}, async () => {
39
+ throw new Error('boom');
40
+ }));
41
+
42
+ const result = await registry.execute('failing', {});
43
+ expect(result.isError).toBe(true);
44
+ expect(result.content).toContain('boom');
45
+ });
46
+
47
+ it('should unregister tools', () => {
48
+ const registry = new MCPToolRegistry();
49
+ registry.register(createMCPTool('temp', 'Temp', {}, async () => ({ content: '' })));
50
+ expect(registry.has('temp')).toBe(true);
51
+ registry.unregister('temp');
52
+ expect(registry.has('temp')).toBe(false);
53
+ });
54
+ });
package/tests/oad.test.ts CHANGED
@@ -1,68 +1,68 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { OADSchema } from '../src/schema/oad';
3
-
4
- describe('OAD Schema', () => {
5
- it('should parse a valid OAD document', () => {
6
- const doc = OADSchema.parse({
7
- apiVersion: 'opc/v1',
8
- kind: 'Agent',
9
- metadata: { name: 'test-agent' },
10
- spec: {},
11
- });
12
- expect(doc.metadata.name).toBe('test-agent');
13
- expect(doc.metadata.version).toBe('1.0.0');
14
- expect(doc.spec.model).toBe('deepseek-chat');
15
- });
16
-
17
- it('should reject invalid apiVersion', () => {
18
- expect(() =>
19
- OADSchema.parse({
20
- apiVersion: 'v2',
21
- kind: 'Agent',
22
- metadata: { name: 'test' },
23
- spec: {},
24
- })
25
- ).toThrow();
26
- });
27
-
28
- it('should parse full document with all fields', () => {
29
- const doc = OADSchema.parse({
30
- apiVersion: 'opc/v1',
31
- kind: 'Agent',
32
- metadata: {
33
- name: 'full-agent',
34
- version: '2.0.0',
35
- description: 'A full agent',
36
- author: 'Test',
37
- license: 'MIT',
38
- marketplace: { certified: true, category: 'support' },
39
- },
40
- spec: {
41
- provider: { default: 'openai', allowed: ['openai'] },
42
- model: 'gpt-4',
43
- systemPrompt: 'You are helpful.',
44
- skills: [{ name: 'faq', description: 'FAQ skill' }],
45
- channels: [{ type: 'web', port: 8080 }],
46
- memory: { shortTerm: true, longTerm: true },
47
- dtv: {
48
- trust: { level: 'certified' },
49
- value: { metrics: ['response_time'] },
50
- },
51
- },
52
- });
53
- expect(doc.metadata.version).toBe('2.0.0');
54
- expect(doc.spec.channels[0].port).toBe(8080);
55
- expect(doc.spec.dtv?.trust?.level).toBe('certified');
56
- });
57
-
58
- it('should reject missing metadata.name', () => {
59
- expect(() =>
60
- OADSchema.parse({
61
- apiVersion: 'opc/v1',
62
- kind: 'Agent',
63
- metadata: {},
64
- spec: {},
65
- })
66
- ).toThrow();
67
- });
68
- });
1
+ import { describe, it, expect } from 'vitest';
2
+ import { OADSchema } from '../src/schema/oad';
3
+
4
+ describe('OAD Schema', () => {
5
+ it('should parse a valid OAD document', () => {
6
+ const doc = OADSchema.parse({
7
+ apiVersion: 'opc/v1',
8
+ kind: 'Agent',
9
+ metadata: { name: 'test-agent' },
10
+ spec: {},
11
+ });
12
+ expect(doc.metadata.name).toBe('test-agent');
13
+ expect(doc.metadata.version).toBe('1.0.0');
14
+ expect(doc.spec.model).toBe('deepseek-chat');
15
+ });
16
+
17
+ it('should reject invalid apiVersion', () => {
18
+ expect(() =>
19
+ OADSchema.parse({
20
+ apiVersion: 'v2',
21
+ kind: 'Agent',
22
+ metadata: { name: 'test' },
23
+ spec: {},
24
+ })
25
+ ).toThrow();
26
+ });
27
+
28
+ it('should parse full document with all fields', () => {
29
+ const doc = OADSchema.parse({
30
+ apiVersion: 'opc/v1',
31
+ kind: 'Agent',
32
+ metadata: {
33
+ name: 'full-agent',
34
+ version: '2.0.0',
35
+ description: 'A full agent',
36
+ author: 'Test',
37
+ license: 'MIT',
38
+ marketplace: { certified: true, category: 'support' },
39
+ },
40
+ spec: {
41
+ provider: { default: 'openai', allowed: ['openai'] },
42
+ model: 'gpt-4',
43
+ systemPrompt: 'You are helpful.',
44
+ skills: [{ name: 'faq', description: 'FAQ skill' }],
45
+ channels: [{ type: 'web', port: 8080 }],
46
+ memory: { shortTerm: true, longTerm: true },
47
+ dtv: {
48
+ trust: { level: 'certified' },
49
+ value: { metrics: ['response_time'] },
50
+ },
51
+ },
52
+ });
53
+ expect(doc.metadata.version).toBe('2.0.0');
54
+ expect(doc.spec.channels[0].port).toBe(8080);
55
+ expect(doc.spec.dtv?.trust?.level).toBe('certified');
56
+ });
57
+
58
+ it('should reject missing metadata.name', () => {
59
+ expect(() =>
60
+ OADSchema.parse({
61
+ apiVersion: 'opc/v1',
62
+ kind: 'Agent',
63
+ metadata: {},
64
+ spec: {},
65
+ })
66
+ ).toThrow();
67
+ });
68
+ });