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.
- package/CHANGELOG.md +25 -0
- package/README.md +91 -32
- package/dist/channels/email.d.ts +32 -26
- package/dist/channels/email.js +239 -62
- package/dist/channels/feishu.d.ts +21 -6
- package/dist/channels/feishu.js +225 -126
- package/dist/channels/telegram.d.ts +30 -9
- package/dist/channels/telegram.js +125 -33
- package/dist/channels/websocket.d.ts +46 -3
- package/dist/channels/websocket.js +306 -37
- package/dist/channels/wechat.d.ts +33 -13
- package/dist/channels/wechat.js +229 -42
- package/dist/cli.js +1127 -19
- package/dist/core/a2a.d.ts +17 -0
- package/dist/core/a2a.js +43 -1
- package/dist/core/agent.d.ts +39 -0
- package/dist/core/agent.js +228 -3
- package/dist/core/runtime.d.ts +7 -0
- package/dist/core/runtime.js +205 -2
- package/dist/core/sandbox.d.ts +26 -0
- package/dist/core/sandbox.js +117 -0
- package/dist/core/scheduler.d.ts +52 -0
- package/dist/core/scheduler.js +168 -0
- package/dist/core/subagent.d.ts +28 -0
- package/dist/core/subagent.js +65 -0
- package/dist/core/workflow-graph.d.ts +93 -0
- package/dist/core/workflow-graph.js +247 -0
- package/dist/daemon.d.ts +3 -0
- package/dist/daemon.js +134 -0
- package/dist/doctor.d.ts +15 -0
- package/dist/doctor.js +183 -0
- package/dist/eval/index.d.ts +65 -0
- package/dist/eval/index.js +191 -0
- package/dist/index.d.ts +37 -6
- package/dist/index.js +75 -3
- package/dist/plugins/content-filter.d.ts +7 -0
- package/dist/plugins/content-filter.js +25 -0
- package/dist/plugins/index.d.ts +42 -0
- package/dist/plugins/index.js +108 -2
- package/dist/plugins/logger.d.ts +6 -0
- package/dist/plugins/logger.js +20 -0
- package/dist/plugins/rate-limiter.d.ts +7 -0
- package/dist/plugins/rate-limiter.js +35 -0
- package/dist/protocols/a2a/client.d.ts +25 -0
- package/dist/protocols/a2a/client.js +115 -0
- package/dist/protocols/a2a/index.d.ts +6 -0
- package/dist/protocols/a2a/index.js +12 -0
- package/dist/protocols/a2a/server.d.ts +41 -0
- package/dist/protocols/a2a/server.js +295 -0
- package/dist/protocols/a2a/types.d.ts +91 -0
- package/dist/protocols/a2a/types.js +15 -0
- package/dist/protocols/a2a/utils.d.ts +6 -0
- package/dist/protocols/a2a/utils.js +47 -0
- package/dist/protocols/agui/client.d.ts +10 -0
- package/dist/protocols/agui/client.js +75 -0
- package/dist/protocols/agui/index.d.ts +4 -0
- package/dist/protocols/agui/index.js +25 -0
- package/dist/protocols/agui/server.d.ts +37 -0
- package/dist/protocols/agui/server.js +191 -0
- package/dist/protocols/agui/types.d.ts +107 -0
- package/dist/protocols/agui/types.js +17 -0
- package/dist/protocols/index.d.ts +2 -0
- package/dist/protocols/index.js +19 -0
- package/dist/protocols/mcp/agent-tools.d.ts +11 -0
- package/dist/protocols/mcp/agent-tools.js +129 -0
- package/dist/protocols/mcp/index.d.ts +5 -0
- package/dist/protocols/mcp/index.js +11 -0
- package/dist/protocols/mcp/server.d.ts +31 -0
- package/dist/protocols/mcp/server.js +248 -0
- package/dist/protocols/mcp/types.d.ts +92 -0
- package/dist/protocols/mcp/types.js +17 -0
- package/dist/providers/index.d.ts +5 -1
- package/dist/providers/index.js +16 -9
- package/dist/publish/index.d.ts +45 -0
- package/dist/publish/index.js +350 -0
- package/dist/schema/oad.d.ts +859 -67
- package/dist/schema/oad.js +47 -3
- package/dist/security/approval.d.ts +36 -0
- package/dist/security/approval.js +113 -0
- package/dist/security/index.d.ts +4 -0
- package/dist/security/index.js +8 -0
- package/dist/security/keys.d.ts +16 -0
- package/dist/security/keys.js +117 -0
- package/dist/skills/auto-learn.d.ts +28 -0
- package/dist/skills/auto-learn.js +257 -0
- package/dist/studio/server.d.ts +63 -0
- package/dist/studio/server.js +625 -0
- package/dist/studio-ui/index.html +662 -0
- package/dist/telemetry/index.d.ts +93 -0
- package/dist/telemetry/index.js +285 -0
- package/dist/tools/builtin/datetime.d.ts +3 -0
- package/dist/tools/builtin/datetime.js +44 -0
- package/dist/tools/builtin/file.d.ts +3 -0
- package/dist/tools/builtin/file.js +151 -0
- package/dist/tools/builtin/index.d.ts +15 -0
- package/dist/tools/builtin/index.js +30 -0
- package/dist/tools/builtin/shell.d.ts +3 -0
- package/dist/tools/builtin/shell.js +43 -0
- package/dist/tools/builtin/web.d.ts +3 -0
- package/dist/tools/builtin/web.js +37 -0
- package/dist/tools/mcp-client.d.ts +24 -0
- package/dist/tools/mcp-client.js +119 -0
- package/package.json +5 -3
- package/scripts/install.ps1 +31 -0
- package/scripts/install.sh +40 -0
- package/src/channels/email.ts +351 -177
- package/src/channels/feishu.ts +349 -236
- package/src/channels/telegram.ts +212 -90
- package/src/channels/websocket.ts +399 -87
- package/src/channels/wechat.ts +329 -149
- package/src/cli.ts +1201 -20
- package/src/core/a2a.ts +60 -0
- package/src/core/agent.ts +420 -152
- package/src/core/runtime.ts +174 -0
- package/src/core/sandbox.ts +143 -0
- package/src/core/scheduler.ts +187 -0
- package/src/core/subagent.ts +98 -0
- package/src/core/workflow-graph.ts +365 -0
- package/src/daemon.ts +96 -0
- package/src/doctor.ts +156 -0
- package/src/eval/index.ts +211 -0
- package/src/eval/suites/basic.json +16 -0
- package/src/eval/suites/memory.json +12 -0
- package/src/eval/suites/safety.json +14 -0
- package/src/index.ts +65 -6
- package/src/plugins/content-filter.ts +23 -0
- package/src/plugins/index.ts +133 -2
- package/src/plugins/logger.ts +18 -0
- package/src/plugins/rate-limiter.ts +38 -0
- package/src/protocols/a2a/client.ts +132 -0
- package/src/protocols/a2a/index.ts +8 -0
- package/src/protocols/a2a/server.ts +333 -0
- package/src/protocols/a2a/types.ts +88 -0
- package/src/protocols/a2a/utils.ts +50 -0
- package/src/protocols/agui/client.ts +83 -0
- package/src/protocols/agui/index.ts +4 -0
- package/src/protocols/agui/server.ts +218 -0
- package/src/protocols/agui/types.ts +153 -0
- package/src/protocols/index.ts +2 -0
- package/src/protocols/mcp/agent-tools.ts +134 -0
- package/src/protocols/mcp/index.ts +8 -0
- package/src/protocols/mcp/server.ts +262 -0
- package/src/protocols/mcp/types.ts +69 -0
- package/src/providers/index.ts +354 -339
- package/src/publish/index.ts +376 -0
- package/src/schema/oad.ts +204 -154
- package/src/security/approval.ts +131 -0
- package/src/security/index.ts +3 -0
- package/src/security/keys.ts +87 -0
- package/src/skills/auto-learn.ts +262 -0
- package/src/studio/server.ts +629 -0
- package/src/studio-ui/index.html +662 -0
- package/src/telemetry/index.ts +324 -0
- package/src/tools/builtin/datetime.ts +41 -0
- package/src/tools/builtin/file.ts +107 -0
- package/src/tools/builtin/index.ts +28 -0
- package/src/tools/builtin/shell.ts +43 -0
- package/src/tools/builtin/web.ts +35 -0
- package/src/tools/mcp-client.ts +131 -0
- package/src/types/agent-workstation.d.ts +2 -0
- package/tests/a2a-protocol.test.ts +285 -0
- package/tests/agui-protocol.test.ts +246 -0
- package/tests/auto-learn.test.ts +105 -0
- package/tests/builtin-tools.test.ts +83 -0
- package/tests/channels/discord.test.ts +79 -0
- package/tests/channels/email.test.ts +148 -0
- package/tests/channels/feishu.test.ts +123 -0
- package/tests/channels/telegram.test.ts +129 -0
- package/tests/channels/websocket.test.ts +53 -0
- package/tests/channels/wechat.test.ts +170 -0
- package/tests/chat-cli.test.ts +160 -0
- package/tests/cli.test.ts +46 -0
- package/tests/daemon.test.ts +135 -0
- package/tests/deepbrain-wire.test.ts +234 -0
- package/tests/doctor.test.ts +38 -0
- package/tests/eval.test.ts +173 -0
- package/tests/init-role.test.ts +124 -0
- package/tests/mcp-client.test.ts +92 -0
- package/tests/mcp-server.test.ts +178 -0
- package/tests/plugin-a2a-enhanced.test.ts +230 -0
- package/tests/publish.test.ts +231 -0
- package/tests/scheduler.test.ts +200 -0
- package/tests/security-enhanced.test.ts +233 -0
- package/tests/skill-learner.test.ts +161 -0
- package/tests/studio.test.ts +229 -0
- package/tests/subagent.test.ts +193 -0
- package/tests/telegram-discord.test.ts +60 -0
- package/tests/telemetry.test.ts +186 -0
- package/tests/tools/builtin-extended.test.ts +138 -0
- package/tests/workflow-graph.test.ts +279 -0
- package/tutorial/customer-service-agent/README.md +612 -0
- package/tutorial/customer-service-agent/SOUL.md +26 -0
- package/tutorial/customer-service-agent/agent.yaml +63 -0
- package/tutorial/customer-service-agent/package.json +19 -0
- package/tutorial/customer-service-agent/src/index.ts +69 -0
- package/tutorial/customer-service-agent/src/skills/faq.ts +27 -0
- package/tutorial/customer-service-agent/src/skills/ticket.ts +22 -0
- 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
|
+
}
|