opc-agent 0.7.0 → 0.9.0

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 (69) hide show
  1. package/dist/channels/email.d.ts +69 -0
  2. package/dist/channels/email.js +118 -0
  3. package/dist/channels/slack.d.ts +62 -0
  4. package/dist/channels/slack.js +107 -0
  5. package/dist/channels/wechat.d.ts +62 -0
  6. package/dist/channels/wechat.js +104 -0
  7. package/dist/cli.js +45 -17
  8. package/dist/core/analytics-engine.d.ts +51 -0
  9. package/dist/core/analytics-engine.js +186 -0
  10. package/dist/core/cache.d.ts +47 -0
  11. package/dist/core/cache.js +156 -0
  12. package/dist/core/compose.d.ts +35 -0
  13. package/dist/core/compose.js +49 -0
  14. package/dist/core/orchestrator.d.ts +68 -0
  15. package/dist/core/orchestrator.js +145 -0
  16. package/dist/core/rate-limiter.d.ts +47 -0
  17. package/dist/core/rate-limiter.js +92 -0
  18. package/dist/i18n/index.d.ts +6 -1
  19. package/dist/i18n/index.js +86 -0
  20. package/dist/index.d.ts +24 -0
  21. package/dist/index.js +39 -1
  22. package/dist/templates/data-analyst.d.ts +53 -0
  23. package/dist/templates/data-analyst.js +70 -0
  24. package/dist/templates/teacher.d.ts +58 -0
  25. package/dist/templates/teacher.js +78 -0
  26. package/dist/testing/index.d.ts +37 -0
  27. package/dist/testing/index.js +176 -0
  28. package/dist/tools/calculator.d.ts +7 -0
  29. package/dist/tools/calculator.js +70 -0
  30. package/dist/tools/datetime.d.ts +7 -0
  31. package/dist/tools/datetime.js +159 -0
  32. package/dist/tools/json-transform.d.ts +7 -0
  33. package/dist/tools/json-transform.js +184 -0
  34. package/dist/tools/text-analysis.d.ts +8 -0
  35. package/dist/tools/text-analysis.js +113 -0
  36. package/docs/.vitepress/config.ts +92 -0
  37. package/docs/api/cli.md +48 -0
  38. package/docs/api/sdk.md +80 -0
  39. package/docs/guide/configuration.md +79 -0
  40. package/docs/guide/deployment.md +42 -0
  41. package/docs/guide/testing.md +84 -0
  42. package/docs/index.md +27 -0
  43. package/docs/zh/api/oad-schema.md +3 -0
  44. package/docs/zh/guide/concepts.md +28 -0
  45. package/docs/zh/guide/configuration.md +39 -0
  46. package/docs/zh/guide/deployment.md +3 -0
  47. package/docs/zh/guide/getting-started.md +58 -0
  48. package/docs/zh/guide/templates.md +22 -0
  49. package/docs/zh/guide/testing.md +18 -0
  50. package/docs/zh/index.md +27 -0
  51. package/package.json +7 -3
  52. package/src/channels/email.ts +177 -0
  53. package/src/channels/slack.ts +160 -0
  54. package/src/channels/wechat.ts +149 -0
  55. package/src/cli.ts +45 -19
  56. package/src/core/analytics-engine.ts +186 -0
  57. package/src/core/cache.ts +141 -0
  58. package/src/core/compose.ts +77 -0
  59. package/src/core/orchestrator.ts +215 -0
  60. package/src/core/rate-limiter.ts +128 -0
  61. package/src/i18n/index.ts +87 -1
  62. package/src/index.ts +28 -0
  63. package/src/templates/data-analyst.ts +70 -0
  64. package/src/templates/teacher.ts +79 -0
  65. package/src/testing/index.ts +181 -0
  66. package/src/tools/calculator.ts +73 -0
  67. package/src/tools/datetime.ts +149 -0
  68. package/src/tools/json-transform.ts +187 -0
  69. package/src/tools/text-analysis.ts +116 -0
@@ -0,0 +1,215 @@
1
+ import { EventEmitter } from 'events';
2
+ import type { AgentContext, Message } from './types';
3
+
4
+ /**
5
+ * Multi-Agent Orchestrator — v0.8.0
6
+ * Routes messages to specialized sub-agents, supports parallel execution and handoffs.
7
+ */
8
+
9
+ export interface AgentNode {
10
+ id: string;
11
+ name: string;
12
+ description: string;
13
+ /** Patterns or intents this agent handles */
14
+ routes: string[];
15
+ /** Function that processes a message and returns a response */
16
+ handler: (context: AgentContext, message: Message) => Promise<Message>;
17
+ /** Priority for routing conflicts (higher wins) */
18
+ priority?: number;
19
+ }
20
+
21
+ export interface OrchestratorWorkflow {
22
+ name: string;
23
+ description?: string;
24
+ /** Ordered list of agent IDs for sequential execution */
25
+ steps?: string[];
26
+ /** List of agent IDs for parallel execution */
27
+ parallel?: string[];
28
+ /** Router config: auto-route based on message content */
29
+ router?: {
30
+ agents: string[];
31
+ fallback?: string;
32
+ };
33
+ }
34
+
35
+ export interface HandoffRequest {
36
+ fromAgent: string;
37
+ toAgent: string;
38
+ context: AgentContext;
39
+ reason: string;
40
+ }
41
+
42
+ export interface OrchestratorConfig {
43
+ agents: AgentNode[];
44
+ workflows?: OrchestratorWorkflow[];
45
+ defaultWorkflow?: string;
46
+ maxParallel?: number;
47
+ }
48
+
49
+ export class Orchestrator extends EventEmitter {
50
+ private agents: Map<string, AgentNode> = new Map();
51
+ private workflows: Map<string, OrchestratorWorkflow> = new Map();
52
+ private defaultWorkflow?: string;
53
+ private maxParallel: number;
54
+
55
+ constructor(config: OrchestratorConfig) {
56
+ super();
57
+ this.maxParallel = config.maxParallel ?? 5;
58
+ this.defaultWorkflow = config.defaultWorkflow;
59
+
60
+ for (const agent of config.agents) {
61
+ this.agents.set(agent.id, agent);
62
+ }
63
+ for (const wf of config.workflows ?? []) {
64
+ this.workflows.set(wf.name, wf);
65
+ }
66
+ }
67
+
68
+ /** Register a new agent node */
69
+ registerAgent(agent: AgentNode): void {
70
+ this.agents.set(agent.id, agent);
71
+ this.emit('agent:registered', agent.id);
72
+ }
73
+
74
+ /** Unregister an agent */
75
+ unregisterAgent(id: string): void {
76
+ this.agents.delete(id);
77
+ this.emit('agent:unregistered', id);
78
+ }
79
+
80
+ /** Route a message to the best-matching agent */
81
+ route(message: Message): AgentNode | undefined {
82
+ const content = message.content.toLowerCase();
83
+ let bestMatch: AgentNode | undefined;
84
+ let bestPriority = -1;
85
+
86
+ for (const agent of this.agents.values()) {
87
+ for (const route of agent.routes) {
88
+ if (content.includes(route.toLowerCase()) || new RegExp(route, 'i').test(content)) {
89
+ const priority = agent.priority ?? 0;
90
+ if (priority > bestPriority) {
91
+ bestMatch = agent;
92
+ bestPriority = priority;
93
+ }
94
+ break;
95
+ }
96
+ }
97
+ }
98
+ return bestMatch;
99
+ }
100
+
101
+ /** Execute a single agent */
102
+ async executeAgent(agentId: string, context: AgentContext, message: Message): Promise<Message> {
103
+ const agent = this.agents.get(agentId);
104
+ if (!agent) throw new Error(`Agent not found: ${agentId}`);
105
+ this.emit('agent:execute', agentId, message);
106
+ const result = await agent.handler(context, message);
107
+ this.emit('agent:complete', agentId, result);
108
+ return result;
109
+ }
110
+
111
+ /** Run multiple agents in parallel */
112
+ async executeParallel(
113
+ agentIds: string[],
114
+ context: AgentContext,
115
+ message: Message
116
+ ): Promise<Map<string, Message>> {
117
+ const results = new Map<string, Message>();
118
+ const batches: string[][] = [];
119
+
120
+ // Batch by maxParallel
121
+ for (let i = 0; i < agentIds.length; i += this.maxParallel) {
122
+ batches.push(agentIds.slice(i, i + this.maxParallel));
123
+ }
124
+
125
+ for (const batch of batches) {
126
+ const promises = batch.map(async (id) => {
127
+ const result = await this.executeAgent(id, context, message);
128
+ results.set(id, result);
129
+ });
130
+ await Promise.all(promises);
131
+ }
132
+
133
+ return results;
134
+ }
135
+
136
+ /** Execute a named workflow */
137
+ async executeWorkflow(
138
+ workflowName: string,
139
+ context: AgentContext,
140
+ message: Message
141
+ ): Promise<Message[]> {
142
+ const wf = this.workflows.get(workflowName);
143
+ if (!wf) throw new Error(`Workflow not found: ${workflowName}`);
144
+
145
+ const results: Message[] = [];
146
+
147
+ // Sequential steps
148
+ if (wf.steps) {
149
+ let currentMessage = message;
150
+ for (const agentId of wf.steps) {
151
+ const result = await this.executeAgent(agentId, context, currentMessage);
152
+ results.push(result);
153
+ currentMessage = result; // chain output → next input
154
+ }
155
+ }
156
+
157
+ // Parallel execution
158
+ if (wf.parallel) {
159
+ const parallelResults = await this.executeParallel(wf.parallel, context, message);
160
+ results.push(...parallelResults.values());
161
+ }
162
+
163
+ // Router-based
164
+ if (wf.router) {
165
+ const matched = this.route(message);
166
+ const targetId = matched && wf.router.agents.includes(matched.id)
167
+ ? matched.id
168
+ : wf.router.fallback;
169
+ if (targetId) {
170
+ const result = await this.executeAgent(targetId, context, message);
171
+ results.push(result);
172
+ }
173
+ }
174
+
175
+ return results;
176
+ }
177
+
178
+ /** Hand off conversation from one agent to another */
179
+ async handoff(request: HandoffRequest): Promise<Message> {
180
+ this.emit('agent:handoff', request);
181
+ const { toAgent, context } = request;
182
+ const lastMessage = context.messages[context.messages.length - 1];
183
+ if (!lastMessage) throw new Error('No message in context for handoff');
184
+ return this.executeAgent(toAgent, context, lastMessage);
185
+ }
186
+
187
+ /** Process an incoming message using the default workflow or routing */
188
+ async process(context: AgentContext, message: Message): Promise<Message[]> {
189
+ if (this.defaultWorkflow) {
190
+ return this.executeWorkflow(this.defaultWorkflow, context, message);
191
+ }
192
+
193
+ // Fallback: route to single agent
194
+ const agent = this.route(message);
195
+ if (agent) {
196
+ const result = await this.executeAgent(agent.id, context, message);
197
+ return [result];
198
+ }
199
+
200
+ return [{
201
+ id: `orch-${Date.now()}`,
202
+ role: 'assistant',
203
+ content: 'No agent available to handle this request.',
204
+ timestamp: Date.now(),
205
+ }];
206
+ }
207
+
208
+ getAgents(): AgentNode[] {
209
+ return Array.from(this.agents.values());
210
+ }
211
+
212
+ getWorkflows(): OrchestratorWorkflow[] {
213
+ return Array.from(this.workflows.values());
214
+ }
215
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Rate Limiter - Per-user and per-provider rate limiting with queuing.
3
+ */
4
+
5
+ interface RateLimitConfig {
6
+ maxRequests: number;
7
+ windowMs: number;
8
+ }
9
+
10
+ interface RateLimitEntry {
11
+ timestamps: number[];
12
+ queue: Array<{ resolve: () => void; reject: (err: Error) => void }>;
13
+ }
14
+
15
+ export class RateLimiter {
16
+ private userLimits: Map<string, RateLimitEntry> = new Map();
17
+ private providerLimits: Map<string, RateLimitEntry> = new Map();
18
+ private userConfig: RateLimitConfig;
19
+ private providerConfig: RateLimitConfig;
20
+ private maxQueueSize: number;
21
+
22
+ constructor(opts?: {
23
+ userLimit?: RateLimitConfig;
24
+ providerLimit?: RateLimitConfig;
25
+ maxQueueSize?: number;
26
+ }) {
27
+ this.userConfig = opts?.userLimit ?? { maxRequests: 60, windowMs: 60_000 };
28
+ this.providerConfig = opts?.providerLimit ?? { maxRequests: 100, windowMs: 60_000 };
29
+ this.maxQueueSize = opts?.maxQueueSize ?? 50;
30
+ }
31
+
32
+ /**
33
+ * Check if a request is allowed. If not, queues it with backpressure.
34
+ * Returns a promise that resolves when the request can proceed.
35
+ */
36
+ async acquire(userId: string, provider: string): Promise<void> {
37
+ await this.checkLimit(userId, this.userLimits, this.userConfig, 'user');
38
+ await this.checkLimit(provider, this.providerLimits, this.providerConfig, 'provider');
39
+ }
40
+
41
+ private async checkLimit(
42
+ key: string,
43
+ limits: Map<string, RateLimitEntry>,
44
+ config: RateLimitConfig,
45
+ type: string,
46
+ ): Promise<void> {
47
+ let entry = limits.get(key);
48
+ if (!entry) {
49
+ entry = { timestamps: [], queue: [] };
50
+ limits.set(key, entry);
51
+ }
52
+
53
+ const now = Date.now();
54
+ // Prune old timestamps
55
+ entry.timestamps = entry.timestamps.filter(t => t > now - config.windowMs);
56
+
57
+ if (entry.timestamps.length < config.maxRequests) {
58
+ entry.timestamps.push(now);
59
+ return;
60
+ }
61
+
62
+ // Rate limited - queue with backpressure
63
+ if (entry.queue.length >= this.maxQueueSize) {
64
+ throw new Error(`Rate limit exceeded for ${type} "${key}" and queue is full (${this.maxQueueSize})`);
65
+ }
66
+
67
+ return new Promise<void>((resolve, reject) => {
68
+ entry!.queue.push({ resolve, reject });
69
+ // Set timeout to process queue when window expires
70
+ const oldestTs = entry!.timestamps[0];
71
+ const waitMs = oldestTs + config.windowMs - now + 10;
72
+ setTimeout(() => {
73
+ this.processQueue(key, limits, config);
74
+ }, Math.max(waitMs, 100));
75
+ });
76
+ }
77
+
78
+ private processQueue(
79
+ key: string,
80
+ limits: Map<string, RateLimitEntry>,
81
+ config: RateLimitConfig,
82
+ ): void {
83
+ const entry = limits.get(key);
84
+ if (!entry || entry.queue.length === 0) return;
85
+
86
+ const now = Date.now();
87
+ entry.timestamps = entry.timestamps.filter(t => t > now - config.windowMs);
88
+
89
+ while (entry.queue.length > 0 && entry.timestamps.length < config.maxRequests) {
90
+ entry.timestamps.push(now);
91
+ const item = entry.queue.shift()!;
92
+ item.resolve();
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Get current usage stats.
98
+ */
99
+ getUsage(userId?: string, provider?: string): {
100
+ user?: { used: number; limit: number; windowMs: number };
101
+ provider?: { used: number; limit: number; windowMs: number };
102
+ } {
103
+ const now = Date.now();
104
+ const result: any = {};
105
+
106
+ if (userId) {
107
+ const entry = this.userLimits.get(userId);
108
+ const used = entry ? entry.timestamps.filter(t => t > now - this.userConfig.windowMs).length : 0;
109
+ result.user = { used, limit: this.userConfig.maxRequests, windowMs: this.userConfig.windowMs };
110
+ }
111
+
112
+ if (provider) {
113
+ const entry = this.providerLimits.get(provider);
114
+ const used = entry ? entry.timestamps.filter(t => t > now - this.providerConfig.windowMs).length : 0;
115
+ result.provider = { used, limit: this.providerConfig.maxRequests, windowMs: this.providerConfig.windowMs };
116
+ }
117
+
118
+ return result;
119
+ }
120
+
121
+ /**
122
+ * Reset all limits.
123
+ */
124
+ reset(): void {
125
+ this.userLimits.clear();
126
+ this.providerLimits.clear();
127
+ }
128
+ }
package/src/i18n/index.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Internationalization (i18n) support for OPC Agent.
3
+ * Supports English, Chinese, and Japanese.
3
4
  */
4
- export type Locale = 'en' | 'zh-CN';
5
+ export type Locale = 'en' | 'zh-CN' | 'ja';
5
6
 
6
7
  export interface I18nMessages {
7
8
  [key: string]: string;
@@ -19,6 +20,7 @@ const messages: Record<Locale, I18nMessages> = {
19
20
  'cli.init.success': 'Created agent project: {name}',
20
21
  'cli.build.success': 'Build successful: {name} v{version}',
21
22
  'cli.test.pass': 'All tests passed',
23
+ 'cli.test.fail': '{count} test(s) failed',
22
24
  'cli.stats.title': 'Agent Analytics',
23
25
  'cli.stats.messages': 'Messages Processed',
24
26
  'cli.stats.avgTime': 'Avg Response Time',
@@ -26,6 +28,24 @@ const messages: Record<Locale, I18nMessages> = {
26
28
  'cli.stats.uptime': 'Uptime',
27
29
  'plugin.loaded': 'Plugin "{name}" loaded',
28
30
  'plugin.error': 'Plugin "{name}" failed: {error}',
31
+ 'web.title': 'OPC Agent',
32
+ 'web.chat.placeholder': 'Type a message...',
33
+ 'web.chat.send': 'Send',
34
+ 'web.nav.chat': 'Chat',
35
+ 'web.nav.dashboard': 'Dashboard',
36
+ 'web.nav.settings': 'Settings',
37
+ 'web.dashboard.title': 'Dashboard',
38
+ 'web.dashboard.messages': 'Messages',
39
+ 'web.dashboard.tokens': 'Tokens Used',
40
+ 'web.dashboard.errors': 'Errors',
41
+ 'web.dashboard.avgLatency': 'Avg Latency',
42
+ 'web.settings.title': 'Settings',
43
+ 'web.settings.language': 'Language',
44
+ 'web.settings.theme': 'Theme',
45
+ 'web.settings.save': 'Save',
46
+ 'cache.hit': 'Cache hit',
47
+ 'cache.miss': 'Cache miss',
48
+ 'rateLimit.exceeded': 'Rate limit exceeded. Please wait.',
29
49
  },
30
50
  'zh-CN': {
31
51
  'agent.started': '智能体 "{name}" 启动成功',
@@ -38,6 +58,7 @@ const messages: Record<Locale, I18nMessages> = {
38
58
  'cli.init.success': '已创建智能体项目: {name}',
39
59
  'cli.build.success': '构建成功: {name} v{version}',
40
60
  'cli.test.pass': '所有测试通过',
61
+ 'cli.test.fail': '{count} 个测试失败',
41
62
  'cli.stats.title': '智能体分析',
42
63
  'cli.stats.messages': '已处理消息',
43
64
  'cli.stats.avgTime': '平均响应时间',
@@ -45,6 +66,62 @@ const messages: Record<Locale, I18nMessages> = {
45
66
  'cli.stats.uptime': '运行时间',
46
67
  'plugin.loaded': '插件 "{name}" 已加载',
47
68
  'plugin.error': '插件 "{name}" 失败: {error}',
69
+ 'web.title': 'OPC 智能体',
70
+ 'web.chat.placeholder': '输入消息...',
71
+ 'web.chat.send': '发送',
72
+ 'web.nav.chat': '对话',
73
+ 'web.nav.dashboard': '仪表盘',
74
+ 'web.nav.settings': '设置',
75
+ 'web.dashboard.title': '仪表盘',
76
+ 'web.dashboard.messages': '消息数',
77
+ 'web.dashboard.tokens': '已用 Token',
78
+ 'web.dashboard.errors': '错误数',
79
+ 'web.dashboard.avgLatency': '平均延迟',
80
+ 'web.settings.title': '设置',
81
+ 'web.settings.language': '语言',
82
+ 'web.settings.theme': '主题',
83
+ 'web.settings.save': '保存',
84
+ 'cache.hit': '缓存命中',
85
+ 'cache.miss': '缓存未命中',
86
+ 'rateLimit.exceeded': '请求过于频繁,请稍候。',
87
+ },
88
+ 'ja': {
89
+ 'agent.started': 'エージェント「{name}」が正常に起動しました',
90
+ 'agent.stopped': 'エージェント「{name}」が停止しました',
91
+ 'agent.error': 'エラーが発生しました: {error}',
92
+ 'agent.greeting': 'こんにちは!何かお手伝いできますか?',
93
+ 'agent.farewell': 'さようなら!良い一日を。',
94
+ 'agent.notUnderstood': '申し訳ございません、理解できませんでした。別の言い方でお願いできますか?',
95
+ 'agent.handoff': 'オペレーターにおつなぎします。',
96
+ 'cli.init.success': 'エージェントプロジェクトを作成しました: {name}',
97
+ 'cli.build.success': 'ビルド成功: {name} v{version}',
98
+ 'cli.test.pass': '全テスト合格',
99
+ 'cli.test.fail': '{count} 件のテストが失敗しました',
100
+ 'cli.stats.title': 'エージェント分析',
101
+ 'cli.stats.messages': '処理済みメッセージ',
102
+ 'cli.stats.avgTime': '平均応答時間',
103
+ 'cli.stats.errors': 'エラー数',
104
+ 'cli.stats.uptime': '稼働時間',
105
+ 'plugin.loaded': 'プラグイン「{name}」を読み込みました',
106
+ 'plugin.error': 'プラグイン「{name}」でエラー: {error}',
107
+ 'web.title': 'OPC エージェント',
108
+ 'web.chat.placeholder': 'メッセージを入力...',
109
+ 'web.chat.send': '送信',
110
+ 'web.nav.chat': 'チャット',
111
+ 'web.nav.dashboard': 'ダッシュボード',
112
+ 'web.nav.settings': '設定',
113
+ 'web.dashboard.title': 'ダッシュボード',
114
+ 'web.dashboard.messages': 'メッセージ数',
115
+ 'web.dashboard.tokens': '使用トークン',
116
+ 'web.dashboard.errors': 'エラー数',
117
+ 'web.dashboard.avgLatency': '平均レイテンシ',
118
+ 'web.settings.title': '設定',
119
+ 'web.settings.language': '言語',
120
+ 'web.settings.theme': 'テーマ',
121
+ 'web.settings.save': '保存',
122
+ 'cache.hit': 'キャッシュヒット',
123
+ 'cache.miss': 'キャッシュミス',
124
+ 'rateLimit.exceeded': 'リクエスト制限を超えました。しばらくお待ちください。',
48
125
  },
49
126
  };
50
127
 
@@ -58,6 +135,14 @@ export function getLocale(): Locale {
58
135
  return currentLocale;
59
136
  }
60
137
 
138
+ export function getSupportedLocales(): { code: Locale; label: string }[] {
139
+ return [
140
+ { code: 'en', label: 'English' },
141
+ { code: 'zh-CN', label: '中文' },
142
+ { code: 'ja', label: '日本語' },
143
+ ];
144
+ }
145
+
61
146
  export function t(key: string, params?: Record<string, string>): string {
62
147
  let msg = messages[currentLocale]?.[key] ?? messages['en']?.[key] ?? key;
63
148
  if (params) {
@@ -71,6 +156,7 @@ export function t(key: string, params?: Record<string, string>): string {
71
156
  export function detectLocale(): Locale {
72
157
  const env = process.env.LANG ?? process.env.LC_ALL ?? process.env.LANGUAGE ?? '';
73
158
  if (env.startsWith('zh')) return 'zh-CN';
159
+ if (env.startsWith('ja')) return 'ja';
74
160
  return 'en';
75
161
  }
76
162
 
package/src/index.ts CHANGED
@@ -58,6 +58,22 @@ export type { AgentManifest, PublishOptions, InstallOptions } from './marketplac
58
58
  // v0.7.0 modules
59
59
  export { createAuthMiddleware, getActiveSessions } from './core/auth';
60
60
  export type { AuthConfig, AuthSession } from './core/auth';
61
+ // v0.8.0 modules
62
+ export { Orchestrator } from './core/orchestrator';
63
+ export type { AgentNode, OrchestratorWorkflow, OrchestratorConfig, HandoffRequest } from './core/orchestrator';
64
+ export { AgentPipeline, compose } from './core/compose';
65
+ export type { ComposableAgent, ComposeOptions } from './core/compose';
66
+ export { EmailChannel } from './channels/email';
67
+ export type { EmailChannelConfig, EmailMessage } from './channels/email';
68
+ export { SlackChannel } from './channels/slack';
69
+ export type { SlackChannelConfig, SlashCommandConfig, SlashCommandPayload } from './channels/slack';
70
+ export { WeChatChannel } from './channels/wechat';
71
+ export type { WeChatChannelConfig, WeChatMessage, TemplateMessageData } from './channels/wechat';
72
+ export { CalculatorTool } from './tools/calculator';
73
+ export { DateTimeTool } from './tools/datetime';
74
+ export { JsonTransformTool } from './tools/json-transform';
75
+ export { TextAnalysisTool } from './tools/text-analysis';
76
+
61
77
  export { HttpSkill } from './skills/http';
62
78
  export { WebhookTriggerSkill } from './skills/webhook-trigger';
63
79
  export type { WebhookTarget } from './skills/webhook-trigger';
@@ -65,3 +81,15 @@ export { SchedulerSkill } from './skills/scheduler';
65
81
  export type { ScheduledTask } from './skills/scheduler';
66
82
  export { DocumentSkill } from './skills/document';
67
83
  export type { DocumentChunk } from './skills/document';
84
+
85
+ // v0.9.0 modules
86
+ export { runTests, loadTestCases, formatReport } from './testing';
87
+ export type { TestCase, TestResult, TestReport } from './testing';
88
+ export { AnalyticsEngine } from './core/analytics-engine';
89
+ export type { AnalyticsEvent, AnalyticsStats } from './core/analytics-engine';
90
+ export { RateLimiter } from './core/rate-limiter';
91
+ export { LLMCache } from './core/cache';
92
+ export type { CacheConfig, CacheEntry } from './core/cache';
93
+ export { getSupportedLocales } from './i18n';
94
+ export { createDataAnalystConfig } from './templates/data-analyst';
95
+ export { createTeacherConfig } from './templates/teacher';
@@ -0,0 +1,70 @@
1
+ import { BaseSkill } from '../skills/base';
2
+ import type { AgentContext, Message, SkillResult } from '../core/types';
3
+
4
+ export class DataQuerySkill extends BaseSkill {
5
+ name = 'data-query';
6
+ description = 'Help users query and analyze data';
7
+
8
+ async execute(_context: AgentContext, message: Message): Promise<SkillResult> {
9
+ const lower = message.content.toLowerCase();
10
+ if (lower.includes('average') || lower.includes('mean') || lower.includes('sum') || lower.includes('count')) {
11
+ return this.match('I can help with that calculation. Could you share the dataset or describe the data source?', 0.8);
12
+ }
13
+ if (lower.includes('chart') || lower.includes('graph') || lower.includes('visualize') || lower.includes('plot')) {
14
+ return this.match('I can help create visualizations. What type of chart would you like? (bar, line, pie, scatter)', 0.85);
15
+ }
16
+ if (lower.includes('csv') || lower.includes('excel') || lower.includes('spreadsheet')) {
17
+ return this.match('I can analyze CSV/Excel data. Please share the file or paste the data.', 0.8);
18
+ }
19
+ return this.noMatch();
20
+ }
21
+ }
22
+
23
+ export class InsightSkill extends BaseSkill {
24
+ name = 'insight-generator';
25
+ description = 'Generate insights from data patterns';
26
+
27
+ async execute(_context: AgentContext, message: Message): Promise<SkillResult> {
28
+ const lower = message.content.toLowerCase();
29
+ if (lower.includes('trend') || lower.includes('pattern') || lower.includes('insight') || lower.includes('anomaly')) {
30
+ return this.match('I\'ll analyze the data for trends and anomalies. Please provide the dataset or describe what you\'re looking for.', 0.8);
31
+ }
32
+ return this.noMatch();
33
+ }
34
+ }
35
+
36
+ export const DATA_ANALYST_SYSTEM_PROMPT = `You are a professional data analyst assistant. Your goals:
37
+ 1. Help users query, transform, and analyze data
38
+ 2. Create clear visualizations and summaries
39
+ 3. Identify trends, patterns, and anomalies
40
+ 4. Explain findings in plain language
41
+ Be precise with numbers, always cite your data source, and suggest next steps for deeper analysis.`;
42
+
43
+ export function createDataAnalystConfig() {
44
+ return {
45
+ apiVersion: 'opc/v1' as const,
46
+ kind: 'Agent' as const,
47
+ metadata: {
48
+ name: 'data-analyst',
49
+ version: '1.0.0',
50
+ description: 'AI data analyst with data querying, visualization, and insight generation',
51
+ author: 'OPC Agent',
52
+ license: 'Apache-2.0',
53
+ },
54
+ spec: {
55
+ provider: { default: 'openai', allowed: ['openai', 'deepseek', 'qwen'] },
56
+ model: 'gpt-4o-mini',
57
+ systemPrompt: DATA_ANALYST_SYSTEM_PROMPT,
58
+ skills: [
59
+ { name: 'data-query', description: 'Query and transform data' },
60
+ { name: 'insight-generator', description: 'Generate data insights' },
61
+ ],
62
+ channels: [{ type: 'web' as const, port: 3000 }],
63
+ memory: { shortTerm: true, longTerm: true },
64
+ dtv: {
65
+ trust: { level: 'sandbox' as const },
66
+ value: { metrics: ['queries_processed', 'insights_generated'] },
67
+ },
68
+ },
69
+ };
70
+ }
@@ -0,0 +1,79 @@
1
+ import { BaseSkill } from '../skills/base';
2
+ import type { AgentContext, Message, SkillResult } from '../core/types';
3
+
4
+ export class LessonPlanSkill extends BaseSkill {
5
+ name = 'lesson-plan';
6
+ description = 'Create and manage lesson plans';
7
+
8
+ async execute(_context: AgentContext, message: Message): Promise<SkillResult> {
9
+ const lower = message.content.toLowerCase();
10
+ if (lower.includes('lesson') || lower.includes('plan') || lower.includes('curriculum') || lower.includes('syllabus')) {
11
+ return this.match('I can help create a lesson plan. What subject, grade level, and learning objectives should I focus on?', 0.85);
12
+ }
13
+ return this.noMatch();
14
+ }
15
+ }
16
+
17
+ export class QuizSkill extends BaseSkill {
18
+ name = 'quiz-generator';
19
+ description = 'Generate quizzes and assessments';
20
+
21
+ async execute(_context: AgentContext, message: Message): Promise<SkillResult> {
22
+ const lower = message.content.toLowerCase();
23
+ if (lower.includes('quiz') || lower.includes('test') || lower.includes('assessment') || lower.includes('exam') || lower.includes('question')) {
24
+ return this.match('I\'ll create a quiz for you. What topic, difficulty level, and number of questions would you like?', 0.85);
25
+ }
26
+ return this.noMatch();
27
+ }
28
+ }
29
+
30
+ export class ExplainSkill extends BaseSkill {
31
+ name = 'concept-explainer';
32
+ description = 'Explain concepts at appropriate level';
33
+
34
+ async execute(_context: AgentContext, message: Message): Promise<SkillResult> {
35
+ const lower = message.content.toLowerCase();
36
+ if (lower.includes('explain') || lower.includes('what is') || lower.includes('how does') || lower.includes('why')) {
37
+ return this.match('Let me explain that concept. What\'s your current level of understanding so I can tailor my explanation?', 0.75);
38
+ }
39
+ return this.noMatch();
40
+ }
41
+ }
42
+
43
+ export const TEACHER_SYSTEM_PROMPT = `You are a patient and encouraging teacher assistant. Your goals:
44
+ 1. Create engaging lesson plans tailored to student level
45
+ 2. Generate quizzes and assessments with answer keys
46
+ 3. Explain complex concepts using analogies and examples
47
+ 4. Provide constructive feedback and encouragement
48
+ 5. Adapt teaching style to different learning preferences
49
+ Be patient, use clear language, and always check for understanding. Use the Socratic method when appropriate.`;
50
+
51
+ export function createTeacherConfig() {
52
+ return {
53
+ apiVersion: 'opc/v1' as const,
54
+ kind: 'Agent' as const,
55
+ metadata: {
56
+ name: 'teacher',
57
+ version: '1.0.0',
58
+ description: 'AI teacher assistant with lesson planning, quiz generation, and concept explanation',
59
+ author: 'OPC Agent',
60
+ license: 'Apache-2.0',
61
+ },
62
+ spec: {
63
+ provider: { default: 'openai', allowed: ['openai', 'deepseek', 'qwen'] },
64
+ model: 'gpt-4o-mini',
65
+ systemPrompt: TEACHER_SYSTEM_PROMPT,
66
+ skills: [
67
+ { name: 'lesson-plan', description: 'Create lesson plans' },
68
+ { name: 'quiz-generator', description: 'Generate quizzes' },
69
+ { name: 'concept-explainer', description: 'Explain concepts' },
70
+ ],
71
+ channels: [{ type: 'web' as const, port: 3000 }],
72
+ memory: { shortTerm: true, longTerm: true },
73
+ dtv: {
74
+ trust: { level: 'sandbox' as const },
75
+ value: { metrics: ['lessons_created', 'quizzes_generated', 'concepts_explained'] },
76
+ },
77
+ },
78
+ };
79
+ }