opc-agent 1.4.0 → 2.0.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 (198) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +91 -32
  3. package/dist/channels/email.d.ts +32 -26
  4. package/dist/channels/email.js +239 -62
  5. package/dist/channels/feishu.d.ts +21 -6
  6. package/dist/channels/feishu.js +225 -126
  7. package/dist/channels/telegram.d.ts +30 -9
  8. package/dist/channels/telegram.js +125 -33
  9. package/dist/channels/websocket.d.ts +46 -3
  10. package/dist/channels/websocket.js +306 -37
  11. package/dist/channels/wechat.d.ts +33 -13
  12. package/dist/channels/wechat.js +229 -42
  13. package/dist/cli.js +1127 -19
  14. package/dist/core/a2a.d.ts +17 -0
  15. package/dist/core/a2a.js +43 -1
  16. package/dist/core/agent.d.ts +39 -0
  17. package/dist/core/agent.js +228 -3
  18. package/dist/core/runtime.d.ts +7 -0
  19. package/dist/core/runtime.js +205 -2
  20. package/dist/core/sandbox.d.ts +26 -0
  21. package/dist/core/sandbox.js +117 -0
  22. package/dist/core/scheduler.d.ts +52 -0
  23. package/dist/core/scheduler.js +168 -0
  24. package/dist/core/subagent.d.ts +28 -0
  25. package/dist/core/subagent.js +65 -0
  26. package/dist/core/workflow-graph.d.ts +93 -0
  27. package/dist/core/workflow-graph.js +247 -0
  28. package/dist/daemon.d.ts +3 -0
  29. package/dist/daemon.js +134 -0
  30. package/dist/doctor.d.ts +15 -0
  31. package/dist/doctor.js +183 -0
  32. package/dist/eval/index.d.ts +65 -0
  33. package/dist/eval/index.js +191 -0
  34. package/dist/index.d.ts +37 -6
  35. package/dist/index.js +75 -3
  36. package/dist/plugins/content-filter.d.ts +7 -0
  37. package/dist/plugins/content-filter.js +25 -0
  38. package/dist/plugins/index.d.ts +42 -0
  39. package/dist/plugins/index.js +108 -2
  40. package/dist/plugins/logger.d.ts +6 -0
  41. package/dist/plugins/logger.js +20 -0
  42. package/dist/plugins/rate-limiter.d.ts +7 -0
  43. package/dist/plugins/rate-limiter.js +35 -0
  44. package/dist/protocols/a2a/client.d.ts +25 -0
  45. package/dist/protocols/a2a/client.js +115 -0
  46. package/dist/protocols/a2a/index.d.ts +6 -0
  47. package/dist/protocols/a2a/index.js +12 -0
  48. package/dist/protocols/a2a/server.d.ts +41 -0
  49. package/dist/protocols/a2a/server.js +295 -0
  50. package/dist/protocols/a2a/types.d.ts +91 -0
  51. package/dist/protocols/a2a/types.js +15 -0
  52. package/dist/protocols/a2a/utils.d.ts +6 -0
  53. package/dist/protocols/a2a/utils.js +47 -0
  54. package/dist/protocols/agui/client.d.ts +10 -0
  55. package/dist/protocols/agui/client.js +75 -0
  56. package/dist/protocols/agui/index.d.ts +4 -0
  57. package/dist/protocols/agui/index.js +25 -0
  58. package/dist/protocols/agui/server.d.ts +37 -0
  59. package/dist/protocols/agui/server.js +191 -0
  60. package/dist/protocols/agui/types.d.ts +107 -0
  61. package/dist/protocols/agui/types.js +17 -0
  62. package/dist/protocols/index.d.ts +2 -0
  63. package/dist/protocols/index.js +19 -0
  64. package/dist/protocols/mcp/agent-tools.d.ts +11 -0
  65. package/dist/protocols/mcp/agent-tools.js +129 -0
  66. package/dist/protocols/mcp/index.d.ts +5 -0
  67. package/dist/protocols/mcp/index.js +11 -0
  68. package/dist/protocols/mcp/server.d.ts +31 -0
  69. package/dist/protocols/mcp/server.js +248 -0
  70. package/dist/protocols/mcp/types.d.ts +92 -0
  71. package/dist/protocols/mcp/types.js +17 -0
  72. package/dist/providers/index.d.ts +5 -1
  73. package/dist/providers/index.js +16 -9
  74. package/dist/publish/index.d.ts +45 -0
  75. package/dist/publish/index.js +350 -0
  76. package/dist/schema/oad.d.ts +859 -67
  77. package/dist/schema/oad.js +47 -3
  78. package/dist/security/approval.d.ts +36 -0
  79. package/dist/security/approval.js +113 -0
  80. package/dist/security/index.d.ts +4 -0
  81. package/dist/security/index.js +8 -0
  82. package/dist/security/keys.d.ts +16 -0
  83. package/dist/security/keys.js +117 -0
  84. package/dist/skills/auto-learn.d.ts +28 -0
  85. package/dist/skills/auto-learn.js +257 -0
  86. package/dist/studio/server.d.ts +63 -0
  87. package/dist/studio/server.js +625 -0
  88. package/dist/studio-ui/index.html +662 -0
  89. package/dist/telemetry/index.d.ts +93 -0
  90. package/dist/telemetry/index.js +285 -0
  91. package/dist/tools/builtin/datetime.d.ts +3 -0
  92. package/dist/tools/builtin/datetime.js +44 -0
  93. package/dist/tools/builtin/file.d.ts +3 -0
  94. package/dist/tools/builtin/file.js +151 -0
  95. package/dist/tools/builtin/index.d.ts +15 -0
  96. package/dist/tools/builtin/index.js +30 -0
  97. package/dist/tools/builtin/shell.d.ts +3 -0
  98. package/dist/tools/builtin/shell.js +43 -0
  99. package/dist/tools/builtin/web.d.ts +3 -0
  100. package/dist/tools/builtin/web.js +37 -0
  101. package/dist/tools/mcp-client.d.ts +24 -0
  102. package/dist/tools/mcp-client.js +119 -0
  103. package/package.json +5 -3
  104. package/scripts/install.ps1 +31 -0
  105. package/scripts/install.sh +40 -0
  106. package/src/channels/email.ts +351 -177
  107. package/src/channels/feishu.ts +349 -236
  108. package/src/channels/telegram.ts +212 -90
  109. package/src/channels/websocket.ts +399 -87
  110. package/src/channels/wechat.ts +329 -149
  111. package/src/cli.ts +1201 -20
  112. package/src/core/a2a.ts +60 -0
  113. package/src/core/agent.ts +420 -152
  114. package/src/core/runtime.ts +174 -0
  115. package/src/core/sandbox.ts +143 -0
  116. package/src/core/scheduler.ts +187 -0
  117. package/src/core/subagent.ts +98 -0
  118. package/src/core/workflow-graph.ts +365 -0
  119. package/src/daemon.ts +96 -0
  120. package/src/doctor.ts +156 -0
  121. package/src/eval/index.ts +211 -0
  122. package/src/eval/suites/basic.json +16 -0
  123. package/src/eval/suites/memory.json +12 -0
  124. package/src/eval/suites/safety.json +14 -0
  125. package/src/index.ts +65 -6
  126. package/src/plugins/content-filter.ts +23 -0
  127. package/src/plugins/index.ts +133 -2
  128. package/src/plugins/logger.ts +18 -0
  129. package/src/plugins/rate-limiter.ts +38 -0
  130. package/src/protocols/a2a/client.ts +132 -0
  131. package/src/protocols/a2a/index.ts +8 -0
  132. package/src/protocols/a2a/server.ts +333 -0
  133. package/src/protocols/a2a/types.ts +88 -0
  134. package/src/protocols/a2a/utils.ts +50 -0
  135. package/src/protocols/agui/client.ts +83 -0
  136. package/src/protocols/agui/index.ts +4 -0
  137. package/src/protocols/agui/server.ts +218 -0
  138. package/src/protocols/agui/types.ts +153 -0
  139. package/src/protocols/index.ts +2 -0
  140. package/src/protocols/mcp/agent-tools.ts +134 -0
  141. package/src/protocols/mcp/index.ts +8 -0
  142. package/src/protocols/mcp/server.ts +262 -0
  143. package/src/protocols/mcp/types.ts +69 -0
  144. package/src/providers/index.ts +354 -339
  145. package/src/publish/index.ts +376 -0
  146. package/src/schema/oad.ts +204 -154
  147. package/src/security/approval.ts +131 -0
  148. package/src/security/index.ts +3 -0
  149. package/src/security/keys.ts +87 -0
  150. package/src/skills/auto-learn.ts +262 -0
  151. package/src/studio/server.ts +629 -0
  152. package/src/studio-ui/index.html +662 -0
  153. package/src/telemetry/index.ts +324 -0
  154. package/src/tools/builtin/datetime.ts +41 -0
  155. package/src/tools/builtin/file.ts +107 -0
  156. package/src/tools/builtin/index.ts +28 -0
  157. package/src/tools/builtin/shell.ts +43 -0
  158. package/src/tools/builtin/web.ts +35 -0
  159. package/src/tools/mcp-client.ts +131 -0
  160. package/src/types/agent-workstation.d.ts +2 -0
  161. package/tests/a2a-protocol.test.ts +285 -0
  162. package/tests/agui-protocol.test.ts +246 -0
  163. package/tests/auto-learn.test.ts +105 -0
  164. package/tests/builtin-tools.test.ts +83 -0
  165. package/tests/channels/discord.test.ts +79 -0
  166. package/tests/channels/email.test.ts +148 -0
  167. package/tests/channels/feishu.test.ts +123 -0
  168. package/tests/channels/telegram.test.ts +129 -0
  169. package/tests/channels/websocket.test.ts +53 -0
  170. package/tests/channels/wechat.test.ts +170 -0
  171. package/tests/chat-cli.test.ts +160 -0
  172. package/tests/cli.test.ts +46 -0
  173. package/tests/daemon.test.ts +135 -0
  174. package/tests/deepbrain-wire.test.ts +234 -0
  175. package/tests/doctor.test.ts +38 -0
  176. package/tests/eval.test.ts +173 -0
  177. package/tests/init-role.test.ts +124 -0
  178. package/tests/mcp-client.test.ts +92 -0
  179. package/tests/mcp-server.test.ts +178 -0
  180. package/tests/plugin-a2a-enhanced.test.ts +230 -0
  181. package/tests/publish.test.ts +231 -0
  182. package/tests/scheduler.test.ts +200 -0
  183. package/tests/security-enhanced.test.ts +233 -0
  184. package/tests/skill-learner.test.ts +161 -0
  185. package/tests/studio.test.ts +229 -0
  186. package/tests/subagent.test.ts +193 -0
  187. package/tests/telegram-discord.test.ts +60 -0
  188. package/tests/telemetry.test.ts +186 -0
  189. package/tests/tools/builtin-extended.test.ts +138 -0
  190. package/tests/workflow-graph.test.ts +279 -0
  191. package/tutorial/customer-service-agent/README.md +612 -0
  192. package/tutorial/customer-service-agent/SOUL.md +26 -0
  193. package/tutorial/customer-service-agent/agent.yaml +63 -0
  194. package/tutorial/customer-service-agent/package.json +19 -0
  195. package/tutorial/customer-service-agent/src/index.ts +69 -0
  196. package/tutorial/customer-service-agent/src/skills/faq.ts +27 -0
  197. package/tutorial/customer-service-agent/src/skills/ticket.ts +22 -0
  198. package/tutorial/customer-service-agent/tsconfig.json +14 -0
@@ -0,0 +1,262 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import type { Message } from '../core/types';
4
+ import type { LLMProvider } from '../providers';
5
+
6
+ export interface LearnedSkill {
7
+ name: string;
8
+ description: string;
9
+ trigger: string;
10
+ instructions: string;
11
+ examples: string[];
12
+ createdAt: Date;
13
+ usageCount: number;
14
+ lastUsed?: Date;
15
+ version: number;
16
+ }
17
+
18
+ const SKILL_EXTRACTION_PROMPT = `Analyze this conversation and determine if it represents a repeatable task that should be saved as a reusable skill.
19
+
20
+ Criteria for creating a skill:
21
+ 1. The task is specific and well-defined
22
+ 2. It could reasonably happen again
23
+ 3. The solution has clear steps
24
+
25
+ If yes, extract:
26
+ - name: short kebab-case name
27
+ - description: one-line description
28
+ - trigger: regex pattern or keywords that would identify this task
29
+ - instructions: step-by-step instructions to complete the task
30
+ - examples: 2-3 example user inputs that would trigger this skill
31
+
32
+ If this is just casual chat or a one-off question, return null.
33
+
34
+ Respond in JSON format only: { "shouldCreate": boolean, "skill": { "name": string, "description": string, "trigger": string, "instructions": string, "examples": string[] } | null }
35
+
36
+ Conversation:
37
+ `;
38
+
39
+ const SKILL_IMPROVEMENT_PROMPT = `This skill was just used. Based on the outcome, suggest improvements:
40
+
41
+ Current skill:
42
+ `;
43
+
44
+ const SKILL_IMPROVEMENT_SUFFIX = `
45
+
46
+ Respond in JSON only: { "shouldImprove": boolean, "improvements": { "instructions"?: string, "trigger"?: string, "examples"?: string[] } | null }`;
47
+
48
+ export class SkillLearner {
49
+ private skillsDir: string;
50
+ private skills: LearnedSkill[] = [];
51
+ private loaded = false;
52
+
53
+ constructor(skillsDir: string) {
54
+ this.skillsDir = skillsDir;
55
+ }
56
+
57
+ async analyzeForSkillCreation(
58
+ conversation: Message[],
59
+ provider: LLMProvider,
60
+ ): Promise<LearnedSkill | null> {
61
+ const conversationText = conversation
62
+ .map((m) => `${m.role}: ${m.content}`)
63
+ .join('\n');
64
+
65
+ const prompt = SKILL_EXTRACTION_PROMPT + conversationText;
66
+
67
+ try {
68
+ const response = await provider.chat(
69
+ [{ id: 'analysis', role: 'user', content: prompt, timestamp: Date.now() }],
70
+ 'You are a skill extraction assistant. Respond only with valid JSON.',
71
+ );
72
+
73
+ const json = extractJson(response);
74
+ if (!json || !json.shouldCreate || !json.skill) return null;
75
+
76
+ const skill: LearnedSkill = {
77
+ name: json.skill.name,
78
+ description: json.skill.description,
79
+ trigger: json.skill.trigger,
80
+ instructions: json.skill.instructions,
81
+ examples: json.skill.examples || [],
82
+ createdAt: new Date(),
83
+ usageCount: 0,
84
+ version: 1,
85
+ };
86
+
87
+ return skill;
88
+ } catch {
89
+ return null;
90
+ }
91
+ }
92
+
93
+ async saveSkill(skill: LearnedSkill): Promise<void> {
94
+ fs.mkdirSync(this.skillsDir, { recursive: true });
95
+ const filePath = path.join(this.skillsDir, `${skill.name}.md`);
96
+ fs.writeFileSync(filePath, skillToMarkdown(skill), 'utf-8');
97
+
98
+ // Update cache
99
+ const idx = this.skills.findIndex((s) => s.name === skill.name);
100
+ if (idx >= 0) {
101
+ this.skills[idx] = skill;
102
+ } else {
103
+ this.skills.push(skill);
104
+ }
105
+ }
106
+
107
+ async loadLearnedSkills(): Promise<LearnedSkill[]> {
108
+ if (!fs.existsSync(this.skillsDir)) return [];
109
+
110
+ const files = fs.readdirSync(this.skillsDir).filter((f) => f.endsWith('.md'));
111
+ this.skills = files
112
+ .map((f) => {
113
+ try {
114
+ const content = fs.readFileSync(path.join(this.skillsDir, f), 'utf-8');
115
+ return parseSkillMarkdown(content);
116
+ } catch {
117
+ return null;
118
+ }
119
+ })
120
+ .filter((s): s is LearnedSkill => s !== null);
121
+
122
+ this.loaded = true;
123
+ return this.skills;
124
+ }
125
+
126
+ matchSkill(message: string): LearnedSkill | null {
127
+ if (!this.loaded) return null;
128
+
129
+ for (const skill of this.skills) {
130
+ try {
131
+ const regex = new RegExp(skill.trigger, 'i');
132
+ if (regex.test(message)) return skill;
133
+ } catch {
134
+ // Fallback: keyword matching — split on common separators, strip non-word chars
135
+ const keywords = skill.trigger.split(/[\s,;|]+/).map(k => k.replace(/[^\w-]/g, '').toLowerCase()).filter(k => k.length > 2);
136
+ const lower = message.toLowerCase();
137
+ if (keywords.some((kw) => lower.includes(kw))) return skill;
138
+ }
139
+ }
140
+ return null;
141
+ }
142
+
143
+ async improveSkill(
144
+ skill: LearnedSkill,
145
+ conversation: Message[],
146
+ provider: LLMProvider,
147
+ ): Promise<void> {
148
+ const conversationText = conversation
149
+ .map((m) => `${m.role}: ${m.content}`)
150
+ .join('\n');
151
+
152
+ const prompt =
153
+ SKILL_IMPROVEMENT_PROMPT +
154
+ skillToMarkdown(skill) +
155
+ '\n\nConversation where it was used:\n' +
156
+ conversationText +
157
+ SKILL_IMPROVEMENT_SUFFIX;
158
+
159
+ try {
160
+ const response = await provider.chat(
161
+ [{ id: 'improve', role: 'user', content: prompt, timestamp: Date.now() }],
162
+ 'You are a skill improvement assistant. Respond only with valid JSON.',
163
+ );
164
+
165
+ const json = extractJson(response);
166
+ if (!json || !json.shouldImprove || !json.improvements) return;
167
+
168
+ const { improvements } = json;
169
+ if (improvements.instructions) skill.instructions = improvements.instructions;
170
+ if (improvements.trigger) skill.trigger = improvements.trigger;
171
+ if (improvements.examples) skill.examples = [...skill.examples, ...improvements.examples];
172
+ skill.version++;
173
+
174
+ await this.saveSkill(skill);
175
+ } catch {
176
+ // Silently fail — improvement is best-effort
177
+ }
178
+ }
179
+
180
+ getSkills(): LearnedSkill[] {
181
+ return [...this.skills];
182
+ }
183
+ }
184
+
185
+ // ─── Helpers ────────────────────────────────────────────────
186
+
187
+ function extractJson(text: string): any {
188
+ // Try to extract JSON from response (may be wrapped in markdown code block)
189
+ const match = text.match(/\{[\s\S]*\}/);
190
+ if (!match) return null;
191
+ try {
192
+ return JSON.parse(match[0]);
193
+ } catch {
194
+ return null;
195
+ }
196
+ }
197
+
198
+ export function skillToMarkdown(skill: LearnedSkill): string {
199
+ const lines = [
200
+ `# Skill: ${skill.name}`,
201
+ '',
202
+ '## Description',
203
+ skill.description,
204
+ '',
205
+ '## Trigger',
206
+ `Pattern: ${skill.trigger}`,
207
+ '',
208
+ '## Instructions',
209
+ skill.instructions,
210
+ '',
211
+ '## Examples',
212
+ ...skill.examples.map((e) => `- "${e}"`),
213
+ '',
214
+ '## Metadata',
215
+ `- Created: ${skill.createdAt.toISOString()}`,
216
+ `- Version: ${skill.version}`,
217
+ `- Usage Count: ${skill.usageCount}`,
218
+ `- Last Used: ${skill.lastUsed?.toISOString() ?? 'never'}`,
219
+ '',
220
+ ];
221
+ return lines.join('\n');
222
+ }
223
+
224
+ export function parseSkillMarkdown(content: string): LearnedSkill | null {
225
+ const nameMatch = content.match(/^# Skill:\s*(.+)$/m);
226
+ if (!nameMatch) return null;
227
+
228
+ const section = (heading: string): string => {
229
+ const re = new RegExp(`## ${heading}\\s*\\n([\\s\\S]*?)(?=\\n## |$)`);
230
+ const m = content.match(re);
231
+ return m ? m[1].trim() : '';
232
+ };
233
+
234
+ const description = section('Description');
235
+ const triggerLine = section('Trigger');
236
+ const trigger = triggerLine.replace(/^Pattern:\s*/i, '').trim();
237
+ const instructions = section('Instructions');
238
+
239
+ const examplesRaw = section('Examples');
240
+ const examples = examplesRaw
241
+ .split('\n')
242
+ .map((l) => l.replace(/^-\s*"?|"?\s*$/g, '').trim())
243
+ .filter(Boolean);
244
+
245
+ const metadata = section('Metadata');
246
+ const getMeta = (key: string): string => {
247
+ const m = metadata.match(new RegExp(`- ${key}:\\s*(.+)`, 'i'));
248
+ return m ? m[1].trim() : '';
249
+ };
250
+
251
+ return {
252
+ name: nameMatch[1].trim(),
253
+ description,
254
+ trigger,
255
+ instructions,
256
+ examples,
257
+ createdAt: new Date(getMeta('Created') || Date.now()),
258
+ version: parseInt(getMeta('Version') || '1', 10),
259
+ usageCount: parseInt(getMeta('Usage Count') || '0', 10),
260
+ lastUsed: getMeta('Last Used') !== 'never' ? new Date(getMeta('Last Used')) : undefined,
261
+ };
262
+ }