opc-agent 4.0.44 → 4.1.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/.github/ISSUE_TEMPLATE/bug_report.md +20 -20
- package/.github/ISSUE_TEMPLATE/feature_request.md +14 -14
- package/.github/PULL_REQUEST_TEMPLATE.md +13 -13
- package/CHANGELOG.md +48 -48
- package/CONTRIBUTING.md +36 -36
- package/README.zh-CN.md +497 -497
- package/dist/channels/wechat.js +6 -6
- package/dist/cli.js +2 -2
- package/dist/core/runtime.js +18 -0
- package/dist/deploy/index.js +56 -56
- package/dist/providers/index.js +39 -13
- package/dist/studio/server.js +211 -20
- package/dist/studio-ui/index.html +279 -24
- package/dist/ui/components.js +105 -105
- package/examples/README.md +22 -22
- package/examples/basic-agent.ts +90 -90
- package/examples/brain-integration.ts +71 -71
- package/examples/multi-channel.ts +74 -74
- package/fix-sidebar.mjs +188 -188
- package/install.ps1 +154 -154
- package/install.sh +164 -164
- package/package.json +1 -1
- package/scripts/install.ps1 +31 -31
- package/scripts/install.sh +40 -40
- package/serve-studio.js +13 -13
- package/serve-test.js +25 -25
- package/src/channels/dingtalk.ts +46 -46
- package/src/channels/email.ts +351 -351
- package/src/channels/feishu.ts +349 -349
- package/src/channels/googlechat.ts +42 -42
- package/src/channels/imessage.ts +31 -31
- package/src/channels/irc.ts +82 -82
- package/src/channels/line.ts +32 -32
- package/src/channels/matrix.ts +33 -33
- package/src/channels/mattermost.ts +57 -57
- package/src/channels/msteams.ts +32 -32
- package/src/channels/nostr.ts +32 -32
- package/src/channels/qq.ts +33 -33
- package/src/channels/signal.ts +32 -32
- package/src/channels/sms.ts +33 -33
- package/src/channels/telegram.ts +616 -616
- package/src/channels/twitch.ts +65 -65
- package/src/channels/voice-call.ts +100 -100
- package/src/channels/websocket.ts +399 -399
- package/src/channels/wechat.ts +329 -329
- package/src/channels/whatsapp.ts +32 -32
- package/src/cli/chat.ts +99 -99
- package/src/cli/setup.ts +314 -314
- package/src/cli.ts +2 -2
- package/src/core/agent.ts +476 -476
- package/src/core/api-server.ts +277 -277
- package/src/core/audio.ts +98 -98
- package/src/core/collaboration.ts +275 -275
- package/src/core/context-discovery.ts +85 -85
- package/src/core/context-refs.ts +140 -140
- package/src/core/gateway.ts +106 -106
- package/src/core/heartbeat.ts +51 -51
- package/src/core/hooks.ts +105 -105
- package/src/core/ide-bridge.ts +133 -133
- package/src/core/node-network.ts +86 -86
- package/src/core/profiles.ts +122 -122
- package/src/core/runtime.ts +18 -0
- package/src/core/scheduler.ts +187 -187
- package/src/core/session-manager.ts +137 -137
- package/src/core/subagent.ts +98 -98
- package/src/core/vision.ts +180 -180
- package/src/core/workflow-graph.ts +365 -365
- package/src/daemon.ts +96 -96
- package/src/deploy/index.ts +255 -255
- package/src/doctor.ts +156 -156
- package/src/eval/index.ts +211 -211
- package/src/eval/suites/basic.json +16 -16
- package/src/eval/suites/memory.json +12 -12
- package/src/eval/suites/safety.json +14 -14
- package/src/hub/brain-seed.ts +54 -54
- package/src/hub/client.ts +60 -60
- package/src/mcp/servers/calculator-mcp.ts +65 -65
- package/src/mcp/servers/crypto-mcp.ts +73 -73
- package/src/mcp/servers/database-mcp.ts +72 -72
- package/src/mcp/servers/datetime-mcp.ts +69 -69
- package/src/mcp/servers/filesystem.ts +66 -66
- package/src/mcp/servers/github-mcp.ts +58 -58
- package/src/mcp/servers/index.ts +63 -63
- package/src/mcp/servers/json-mcp.ts +102 -102
- package/src/mcp/servers/memory-mcp.ts +56 -56
- package/src/mcp/servers/regex-mcp.ts +53 -53
- package/src/mcp/servers/web-mcp.ts +49 -49
- package/src/memory/context-compressor.ts +189 -189
- package/src/memory/seed-loader.ts +212 -212
- package/src/memory/user-profiler.ts +215 -215
- package/src/plugins/content-filter.ts +23 -23
- package/src/plugins/logger.ts +18 -18
- package/src/plugins/rate-limiter.ts +38 -38
- package/src/protocols/a2a/client.ts +132 -132
- package/src/protocols/a2a/index.ts +8 -8
- package/src/protocols/a2a/server.ts +333 -333
- package/src/protocols/a2a/types.ts +88 -88
- package/src/protocols/a2a/utils.ts +50 -50
- package/src/protocols/agui/client.ts +83 -83
- package/src/protocols/agui/index.ts +4 -4
- package/src/protocols/agui/server.ts +218 -218
- package/src/protocols/agui/types.ts +153 -153
- package/src/protocols/index.ts +2 -2
- package/src/protocols/mcp/agent-tools.ts +134 -134
- package/src/protocols/mcp/index.ts +8 -8
- package/src/protocols/mcp/server.ts +262 -262
- package/src/protocols/mcp/types.ts +69 -69
- package/src/providers/index.ts +632 -608
- package/src/publish/index.ts +376 -376
- package/src/scheduler/cron-engine.ts +191 -191
- package/src/scheduler/index.ts +2 -2
- package/src/schema/oad.ts +217 -217
- package/src/security/approval.ts +131 -131
- package/src/security/approvals.ts +143 -143
- package/src/security/elevated.ts +105 -105
- package/src/security/guardrails.ts +248 -248
- package/src/security/index.ts +9 -9
- package/src/security/keys.ts +87 -87
- package/src/security/secrets.ts +129 -129
- package/src/skills/builtin/index.ts +408 -408
- package/src/skills/marketplace.ts +113 -113
- package/src/skills/types.ts +42 -42
- package/src/studio/server.ts +209 -22
- package/src/studio/templates-data.ts +178 -178
- package/src/studio-ui/index.html +279 -24
- package/src/telemetry/index.ts +324 -324
- package/src/tools/builtin/browser.ts +299 -299
- package/src/tools/builtin/datetime.ts +41 -41
- package/src/tools/builtin/file.ts +107 -107
- package/src/tools/builtin/home-assistant.ts +116 -116
- package/src/tools/builtin/rl-tools.ts +243 -243
- package/src/tools/builtin/shell.ts +43 -43
- package/src/tools/builtin/vision.ts +64 -64
- package/src/tools/builtin/web-search.ts +126 -126
- package/src/tools/builtin/web.ts +35 -35
- package/src/tools/document-processor.ts +213 -213
- package/src/tools/image-generator.ts +150 -150
- package/src/tools/integrations/calendar.ts +73 -73
- package/src/tools/integrations/code-exec.ts +39 -39
- package/src/tools/integrations/csv-analyzer.ts +92 -92
- package/src/tools/integrations/database.ts +44 -44
- package/src/tools/integrations/email-send.ts +76 -76
- package/src/tools/integrations/git-tool.ts +42 -42
- package/src/tools/integrations/github-tool.ts +76 -76
- package/src/tools/integrations/image-gen.ts +56 -56
- package/src/tools/integrations/index.ts +92 -92
- package/src/tools/integrations/jira.ts +83 -83
- package/src/tools/integrations/notion.ts +71 -71
- package/src/tools/integrations/npm-tool.ts +48 -48
- package/src/tools/integrations/pdf-reader.ts +58 -58
- package/src/tools/integrations/slack.ts +65 -65
- package/src/tools/integrations/summarizer.ts +49 -49
- package/src/tools/integrations/translator.ts +48 -48
- package/src/tools/integrations/trello.ts +60 -60
- package/src/tools/integrations/vector-search.ts +42 -42
- package/src/tools/integrations/web-scraper.ts +47 -47
- package/src/tools/integrations/web-search.ts +58 -58
- package/src/tools/integrations/webhook.ts +38 -38
- package/src/tools/mcp-client.ts +131 -131
- package/src/tools/web-scraper.ts +179 -179
- package/src/tools/web-search.ts +180 -180
- package/src/ui/components.ts +127 -127
- package/srv-out.txt +1 -1
- package/templates/ecommerce-assistant/README.md +45 -45
- package/templates/ecommerce-assistant/oad.yaml +47 -47
- package/templates/tech-support/README.md +43 -43
- package/templates/tech-support/oad.yaml +45 -45
- package/test-agent/Dockerfile +9 -9
- package/test-agent/README.md +50 -50
- package/test-agent/agent.yaml +23 -23
- package/test-agent/docker-compose.yml +11 -11
- package/test-agent/oad.yaml +31 -31
- package/test-agent/package-lock.json +1492 -1492
- package/test-agent/package.json +17 -17
- package/test-agent/src/index.ts +24 -24
- package/test-agent/src/skills/echo.ts +15 -15
- package/test-agent/tsconfig.json +24 -24
- package/test-full.js +43 -43
- package/test-sidebar.js +22 -22
- package/test-studio3.js +75 -75
- package/test-studio4.js +41 -41
- package/tests/a2a-protocol.test.ts +285 -285
- package/tests/agui-protocol.test.ts +246 -246
- package/tests/api-server.test.ts +148 -148
- package/tests/approvals.test.ts +89 -89
- package/tests/audio.test.ts +40 -40
- package/tests/brain-seed-extended.test.ts +490 -490
- package/tests/brain-seed.test.ts +239 -239
- package/tests/browser.test.ts +179 -179
- package/tests/channels/discord.test.ts +79 -79
- package/tests/channels/email.test.ts +148 -148
- package/tests/channels/feishu.test.ts +123 -123
- package/tests/channels/telegram.test.ts +129 -129
- package/tests/channels/websocket.test.ts +53 -53
- package/tests/channels/wechat.test.ts +170 -170
- package/tests/channels-extra.test.ts +45 -45
- package/tests/chat-cli.test.ts +160 -160
- package/tests/cli.test.ts +46 -46
- package/tests/context-compressor.test.ts +172 -172
- package/tests/context-refs.test.ts +121 -121
- package/tests/cron-engine.test.ts +101 -101
- package/tests/daemon.test.ts +135 -135
- package/tests/deepbrain-wire.test.ts +234 -234
- package/tests/deploy-and-dag.test.ts +196 -196
- package/tests/doctor.test.ts +38 -38
- package/tests/document-processor.test.ts +69 -69
- package/tests/e2e-nocode.test.ts +442 -442
- package/tests/elevated.test.ts +69 -69
- package/tests/eval.test.ts +173 -173
- package/tests/gateway.test.ts +63 -63
- package/tests/guardrails.test.ts +177 -177
- package/tests/home-assistant.test.ts +40 -40
- package/tests/hooks.test.ts +79 -79
- package/tests/ide-bridge.test.ts +38 -38
- package/tests/image-generator.test.ts +84 -84
- package/tests/init-role.test.ts +124 -124
- package/tests/integrations.test.ts +249 -249
- package/tests/mcp-client.test.ts +92 -92
- package/tests/mcp-server.test.ts +178 -178
- package/tests/mcp-servers.test.ts +260 -260
- package/tests/node-network.test.ts +74 -74
- package/tests/plugin-a2a-enhanced.test.ts +230 -230
- package/tests/profiles.test.ts +61 -61
- package/tests/publish.test.ts +231 -231
- package/tests/rl-tools.test.ts +93 -93
- package/tests/sandbox-manager.test.ts +46 -46
- package/tests/scheduler.test.ts +200 -200
- package/tests/secrets.test.ts +107 -107
- package/tests/security-enhanced.test.ts +233 -233
- package/tests/settings-api.test.ts +148 -148
- package/tests/setup.test.ts +73 -73
- package/tests/subagent.test.ts +193 -193
- package/tests/telegram-discord.test.ts +60 -60
- package/tests/telemetry.test.ts +186 -186
- package/tests/user-profiler.test.ts +169 -169
- package/tests/v090-features.test.ts +254 -254
- package/tests/vision.test.ts +61 -61
- package/tests/voice-call.test.ts +47 -47
- package/tests/voice-enhanced.test.ts +169 -169
- package/tests/voice-interaction.test.ts +38 -38
- package/tests/web-search.test.ts +155 -155
- package/tests/workflow-graph.test.ts +279 -279
- package/tutorial/customer-service-agent/README.md +612 -612
- package/tutorial/customer-service-agent/SOUL.md +26 -26
- package/tutorial/customer-service-agent/agent.yaml +63 -63
- package/tutorial/customer-service-agent/package.json +19 -19
- package/tutorial/customer-service-agent/src/index.ts +69 -69
- package/tutorial/customer-service-agent/src/skills/faq.ts +27 -27
- package/tutorial/customer-service-agent/src/skills/ticket.ts +22 -22
- package/tutorial/customer-service-agent/tsconfig.json +14 -14
|
@@ -1,243 +1,243 @@
|
|
|
1
|
-
import type { MCPTool, MCPToolResult } from '../mcp';
|
|
2
|
-
|
|
3
|
-
// In-memory storage fallback
|
|
4
|
-
interface Trajectory {
|
|
5
|
-
id: string;
|
|
6
|
-
taskType: string;
|
|
7
|
-
actions: Array<{ action: string; timestamp: number; reward?: number }>;
|
|
8
|
-
outcome?: 'success' | 'partial' | 'failure';
|
|
9
|
-
totalReward: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface PolicyEntry {
|
|
13
|
-
taskType: string;
|
|
14
|
-
preferredActions: string[];
|
|
15
|
-
weights: Record<string, number>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const trajectories: Trajectory[] = [];
|
|
19
|
-
const policies = new Map<string, PolicyEntry>();
|
|
20
|
-
let currentEpisode: Trajectory | null = null;
|
|
21
|
-
|
|
22
|
-
function getOrCreateEpisode(taskType: string): Trajectory {
|
|
23
|
-
if (!currentEpisode || currentEpisode.taskType !== taskType) {
|
|
24
|
-
currentEpisode = {
|
|
25
|
-
id: `ep_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
26
|
-
taskType,
|
|
27
|
-
actions: [],
|
|
28
|
-
totalReward: 0,
|
|
29
|
-
};
|
|
30
|
-
trajectories.push(currentEpisode);
|
|
31
|
-
}
|
|
32
|
-
return currentEpisode;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export const rlRecordTrajectory: MCPTool = {
|
|
36
|
-
name: 'rl_record_trajectory',
|
|
37
|
-
description: 'Record action sequences and outcomes for RL training',
|
|
38
|
-
inputSchema: {
|
|
39
|
-
type: 'object',
|
|
40
|
-
properties: {
|
|
41
|
-
taskType: { type: 'string' },
|
|
42
|
-
action: { type: 'string' },
|
|
43
|
-
outcome: { type: 'string', enum: ['success', 'partial', 'failure'] },
|
|
44
|
-
},
|
|
45
|
-
required: ['taskType', 'action'],
|
|
46
|
-
},
|
|
47
|
-
async execute(input): Promise<MCPToolResult> {
|
|
48
|
-
const ep = getOrCreateEpisode(input.taskType as string);
|
|
49
|
-
ep.actions.push({ action: input.action as string, timestamp: Date.now() });
|
|
50
|
-
if (input.outcome) ep.outcome = input.outcome as Trajectory['outcome'];
|
|
51
|
-
return { content: JSON.stringify({ episodeId: ep.id, actionsRecorded: ep.actions.length }) };
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export const rlEvaluateOutcome: MCPTool = {
|
|
56
|
-
name: 'rl_evaluate_outcome',
|
|
57
|
-
description: "Score an action's outcome (success/partial/failure)",
|
|
58
|
-
inputSchema: {
|
|
59
|
-
type: 'object',
|
|
60
|
-
properties: {
|
|
61
|
-
episodeId: { type: 'string' },
|
|
62
|
-
outcome: { type: 'string', enum: ['success', 'partial', 'failure'] },
|
|
63
|
-
},
|
|
64
|
-
required: ['outcome'],
|
|
65
|
-
},
|
|
66
|
-
async execute(input): Promise<MCPToolResult> {
|
|
67
|
-
const ep = input.episodeId
|
|
68
|
-
? trajectories.find(t => t.id === input.episodeId)
|
|
69
|
-
: currentEpisode;
|
|
70
|
-
if (!ep) return { content: 'No active episode found', isError: true };
|
|
71
|
-
ep.outcome = input.outcome as Trajectory['outcome'];
|
|
72
|
-
const score = ep.outcome === 'success' ? 1 : ep.outcome === 'partial' ? 0.5 : 0;
|
|
73
|
-
return { content: JSON.stringify({ episodeId: ep.id, outcome: ep.outcome, score }) };
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export const rlGetBestStrategy: MCPTool = {
|
|
78
|
-
name: 'rl_get_best_strategy',
|
|
79
|
-
description: 'Retrieve best-performing strategy for a task type',
|
|
80
|
-
inputSchema: {
|
|
81
|
-
type: 'object',
|
|
82
|
-
properties: { taskType: { type: 'string' } },
|
|
83
|
-
required: ['taskType'],
|
|
84
|
-
},
|
|
85
|
-
async execute(input): Promise<MCPToolResult> {
|
|
86
|
-
const taskType = input.taskType as string;
|
|
87
|
-
const relevant = trajectories.filter(t => t.taskType === taskType && t.outcome === 'success');
|
|
88
|
-
if (relevant.length === 0) return { content: JSON.stringify({ strategy: null, message: 'No successful strategies found' }) };
|
|
89
|
-
const best = relevant.reduce((a, b) => a.totalReward >= b.totalReward ? a : b);
|
|
90
|
-
return { content: JSON.stringify({ strategy: best.actions.map(a => a.action), totalReward: best.totalReward }) };
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
export const rlCompareStrategies: MCPTool = {
|
|
95
|
-
name: 'rl_compare_strategies',
|
|
96
|
-
description: 'Compare multiple strategies by success rate',
|
|
97
|
-
inputSchema: {
|
|
98
|
-
type: 'object',
|
|
99
|
-
properties: { taskType: { type: 'string' } },
|
|
100
|
-
required: ['taskType'],
|
|
101
|
-
},
|
|
102
|
-
async execute(input): Promise<MCPToolResult> {
|
|
103
|
-
const taskType = input.taskType as string;
|
|
104
|
-
const relevant = trajectories.filter(t => t.taskType === taskType && t.outcome);
|
|
105
|
-
const total = relevant.length;
|
|
106
|
-
const successes = relevant.filter(t => t.outcome === 'success').length;
|
|
107
|
-
const partials = relevant.filter(t => t.outcome === 'partial').length;
|
|
108
|
-
const failures = relevant.filter(t => t.outcome === 'failure').length;
|
|
109
|
-
return {
|
|
110
|
-
content: JSON.stringify({
|
|
111
|
-
taskType, total, successRate: total ? successes / total : 0,
|
|
112
|
-
breakdown: { successes, partials, failures },
|
|
113
|
-
}),
|
|
114
|
-
};
|
|
115
|
-
},
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
export const rlGenerateTrainingData: MCPTool = {
|
|
119
|
-
name: 'rl_generate_training_data',
|
|
120
|
-
description: 'Export trajectories as fine-tuning JSONL',
|
|
121
|
-
inputSchema: {
|
|
122
|
-
type: 'object',
|
|
123
|
-
properties: { taskType: { type: 'string' }, minReward: { type: 'number' } },
|
|
124
|
-
},
|
|
125
|
-
async execute(input): Promise<MCPToolResult> {
|
|
126
|
-
let data = trajectories;
|
|
127
|
-
if (input.taskType) data = data.filter(t => t.taskType === input.taskType);
|
|
128
|
-
if (input.minReward != null) data = data.filter(t => t.totalReward >= (input.minReward as number));
|
|
129
|
-
const jsonl = data.map(t => JSON.stringify({
|
|
130
|
-
messages: [
|
|
131
|
-
{ role: 'system', content: `Task: ${t.taskType}` },
|
|
132
|
-
...t.actions.map(a => ({ role: 'assistant', content: a.action })),
|
|
133
|
-
],
|
|
134
|
-
outcome: t.outcome,
|
|
135
|
-
reward: t.totalReward,
|
|
136
|
-
})).join('\n');
|
|
137
|
-
return { content: jsonl || '(no data)' };
|
|
138
|
-
},
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
export const rlRewardSignal: MCPTool = {
|
|
142
|
-
name: 'rl_reward_signal',
|
|
143
|
-
description: 'Record positive/negative reward for last action',
|
|
144
|
-
inputSchema: {
|
|
145
|
-
type: 'object',
|
|
146
|
-
properties: { reward: { type: 'number' }, reason: { type: 'string' } },
|
|
147
|
-
required: ['reward'],
|
|
148
|
-
},
|
|
149
|
-
async execute(input): Promise<MCPToolResult> {
|
|
150
|
-
if (!currentEpisode || currentEpisode.actions.length === 0) {
|
|
151
|
-
return { content: 'No current episode or actions to reward', isError: true };
|
|
152
|
-
}
|
|
153
|
-
const lastAction = currentEpisode.actions[currentEpisode.actions.length - 1];
|
|
154
|
-
lastAction.reward = input.reward as number;
|
|
155
|
-
currentEpisode.totalReward += input.reward as number;
|
|
156
|
-
return { content: JSON.stringify({ action: lastAction.action, reward: input.reward, totalReward: currentEpisode.totalReward }) };
|
|
157
|
-
},
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
export const rlExplorationSuggest: MCPTool = {
|
|
161
|
-
name: 'rl_exploration_suggest',
|
|
162
|
-
description: 'Suggest alternative approaches (exploration)',
|
|
163
|
-
inputSchema: {
|
|
164
|
-
type: 'object',
|
|
165
|
-
properties: { taskType: { type: 'string' }, currentAction: { type: 'string' } },
|
|
166
|
-
required: ['taskType'],
|
|
167
|
-
},
|
|
168
|
-
async execute(input): Promise<MCPToolResult> {
|
|
169
|
-
const taskType = input.taskType as string;
|
|
170
|
-
const allActions = new Set<string>();
|
|
171
|
-
trajectories.filter(t => t.taskType === taskType).forEach(t => t.actions.forEach(a => allActions.add(a.action)));
|
|
172
|
-
const suggestions = Array.from(allActions).filter(a => a !== input.currentAction).slice(0, 5);
|
|
173
|
-
if (suggestions.length === 0) {
|
|
174
|
-
return { content: JSON.stringify({ suggestions: [], message: 'No alternative actions found. Try a completely new approach.' }) };
|
|
175
|
-
}
|
|
176
|
-
return { content: JSON.stringify({ suggestions }) };
|
|
177
|
-
},
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
export const rlUpdatePolicy: MCPTool = {
|
|
181
|
-
name: 'rl_update_policy',
|
|
182
|
-
description: "Update agent's action preferences based on rewards",
|
|
183
|
-
inputSchema: {
|
|
184
|
-
type: 'object',
|
|
185
|
-
properties: { taskType: { type: 'string' }, action: { type: 'string' }, weight: { type: 'number' } },
|
|
186
|
-
required: ['taskType', 'action', 'weight'],
|
|
187
|
-
},
|
|
188
|
-
async execute(input): Promise<MCPToolResult> {
|
|
189
|
-
const taskType = input.taskType as string;
|
|
190
|
-
let policy = policies.get(taskType);
|
|
191
|
-
if (!policy) {
|
|
192
|
-
policy = { taskType, preferredActions: [], weights: {} };
|
|
193
|
-
policies.set(taskType, policy);
|
|
194
|
-
}
|
|
195
|
-
const action = input.action as string;
|
|
196
|
-
policy.weights[action] = (policy.weights[action] || 0) + (input.weight as number);
|
|
197
|
-
policy.preferredActions = Object.entries(policy.weights)
|
|
198
|
-
.sort(([, a], [, b]) => b - a)
|
|
199
|
-
.map(([k]) => k);
|
|
200
|
-
return { content: JSON.stringify({ taskType, preferredActions: policy.preferredActions.slice(0, 5), weights: policy.weights }) };
|
|
201
|
-
},
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
export const rlGetStatistics: MCPTool = {
|
|
205
|
-
name: 'rl_get_statistics',
|
|
206
|
-
description: 'Get success/failure stats by task type',
|
|
207
|
-
inputSchema: {
|
|
208
|
-
type: 'object',
|
|
209
|
-
properties: { taskType: { type: 'string' } },
|
|
210
|
-
},
|
|
211
|
-
async execute(input): Promise<MCPToolResult> {
|
|
212
|
-
let data = trajectories;
|
|
213
|
-
if (input.taskType) data = data.filter(t => t.taskType === input.taskType);
|
|
214
|
-
const stats: Record<string, { total: number; success: number; partial: number; failure: number; avgReward: number }> = {};
|
|
215
|
-
for (const t of data) {
|
|
216
|
-
if (!stats[t.taskType]) stats[t.taskType] = { total: 0, success: 0, partial: 0, failure: 0, avgReward: 0 };
|
|
217
|
-
const s = stats[t.taskType];
|
|
218
|
-
s.total++;
|
|
219
|
-
if (t.outcome === 'success') s.success++;
|
|
220
|
-
else if (t.outcome === 'partial') s.partial++;
|
|
221
|
-
else if (t.outcome === 'failure') s.failure++;
|
|
222
|
-
s.avgReward = (s.avgReward * (s.total - 1) + t.totalReward) / s.total;
|
|
223
|
-
}
|
|
224
|
-
return { content: JSON.stringify(stats) };
|
|
225
|
-
},
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
export const rlResetEpisode: MCPTool = {
|
|
229
|
-
name: 'rl_reset_episode',
|
|
230
|
-
description: 'Clear current episode state',
|
|
231
|
-
inputSchema: { type: 'object', properties: {} },
|
|
232
|
-
async execute(): Promise<MCPToolResult> {
|
|
233
|
-
const had = currentEpisode != null;
|
|
234
|
-
currentEpisode = null;
|
|
235
|
-
return { content: JSON.stringify({ reset: true, hadActiveEpisode: had }) };
|
|
236
|
-
},
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
export const rlTools: MCPTool[] = [
|
|
240
|
-
rlRecordTrajectory, rlEvaluateOutcome, rlGetBestStrategy, rlCompareStrategies,
|
|
241
|
-
rlGenerateTrainingData, rlRewardSignal, rlExplorationSuggest, rlUpdatePolicy,
|
|
242
|
-
rlGetStatistics, rlResetEpisode,
|
|
243
|
-
];
|
|
1
|
+
import type { MCPTool, MCPToolResult } from '../mcp';
|
|
2
|
+
|
|
3
|
+
// In-memory storage fallback
|
|
4
|
+
interface Trajectory {
|
|
5
|
+
id: string;
|
|
6
|
+
taskType: string;
|
|
7
|
+
actions: Array<{ action: string; timestamp: number; reward?: number }>;
|
|
8
|
+
outcome?: 'success' | 'partial' | 'failure';
|
|
9
|
+
totalReward: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface PolicyEntry {
|
|
13
|
+
taskType: string;
|
|
14
|
+
preferredActions: string[];
|
|
15
|
+
weights: Record<string, number>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const trajectories: Trajectory[] = [];
|
|
19
|
+
const policies = new Map<string, PolicyEntry>();
|
|
20
|
+
let currentEpisode: Trajectory | null = null;
|
|
21
|
+
|
|
22
|
+
function getOrCreateEpisode(taskType: string): Trajectory {
|
|
23
|
+
if (!currentEpisode || currentEpisode.taskType !== taskType) {
|
|
24
|
+
currentEpisode = {
|
|
25
|
+
id: `ep_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
26
|
+
taskType,
|
|
27
|
+
actions: [],
|
|
28
|
+
totalReward: 0,
|
|
29
|
+
};
|
|
30
|
+
trajectories.push(currentEpisode);
|
|
31
|
+
}
|
|
32
|
+
return currentEpisode;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const rlRecordTrajectory: MCPTool = {
|
|
36
|
+
name: 'rl_record_trajectory',
|
|
37
|
+
description: 'Record action sequences and outcomes for RL training',
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
taskType: { type: 'string' },
|
|
42
|
+
action: { type: 'string' },
|
|
43
|
+
outcome: { type: 'string', enum: ['success', 'partial', 'failure'] },
|
|
44
|
+
},
|
|
45
|
+
required: ['taskType', 'action'],
|
|
46
|
+
},
|
|
47
|
+
async execute(input): Promise<MCPToolResult> {
|
|
48
|
+
const ep = getOrCreateEpisode(input.taskType as string);
|
|
49
|
+
ep.actions.push({ action: input.action as string, timestamp: Date.now() });
|
|
50
|
+
if (input.outcome) ep.outcome = input.outcome as Trajectory['outcome'];
|
|
51
|
+
return { content: JSON.stringify({ episodeId: ep.id, actionsRecorded: ep.actions.length }) };
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const rlEvaluateOutcome: MCPTool = {
|
|
56
|
+
name: 'rl_evaluate_outcome',
|
|
57
|
+
description: "Score an action's outcome (success/partial/failure)",
|
|
58
|
+
inputSchema: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
properties: {
|
|
61
|
+
episodeId: { type: 'string' },
|
|
62
|
+
outcome: { type: 'string', enum: ['success', 'partial', 'failure'] },
|
|
63
|
+
},
|
|
64
|
+
required: ['outcome'],
|
|
65
|
+
},
|
|
66
|
+
async execute(input): Promise<MCPToolResult> {
|
|
67
|
+
const ep = input.episodeId
|
|
68
|
+
? trajectories.find(t => t.id === input.episodeId)
|
|
69
|
+
: currentEpisode;
|
|
70
|
+
if (!ep) return { content: 'No active episode found', isError: true };
|
|
71
|
+
ep.outcome = input.outcome as Trajectory['outcome'];
|
|
72
|
+
const score = ep.outcome === 'success' ? 1 : ep.outcome === 'partial' ? 0.5 : 0;
|
|
73
|
+
return { content: JSON.stringify({ episodeId: ep.id, outcome: ep.outcome, score }) };
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const rlGetBestStrategy: MCPTool = {
|
|
78
|
+
name: 'rl_get_best_strategy',
|
|
79
|
+
description: 'Retrieve best-performing strategy for a task type',
|
|
80
|
+
inputSchema: {
|
|
81
|
+
type: 'object',
|
|
82
|
+
properties: { taskType: { type: 'string' } },
|
|
83
|
+
required: ['taskType'],
|
|
84
|
+
},
|
|
85
|
+
async execute(input): Promise<MCPToolResult> {
|
|
86
|
+
const taskType = input.taskType as string;
|
|
87
|
+
const relevant = trajectories.filter(t => t.taskType === taskType && t.outcome === 'success');
|
|
88
|
+
if (relevant.length === 0) return { content: JSON.stringify({ strategy: null, message: 'No successful strategies found' }) };
|
|
89
|
+
const best = relevant.reduce((a, b) => a.totalReward >= b.totalReward ? a : b);
|
|
90
|
+
return { content: JSON.stringify({ strategy: best.actions.map(a => a.action), totalReward: best.totalReward }) };
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const rlCompareStrategies: MCPTool = {
|
|
95
|
+
name: 'rl_compare_strategies',
|
|
96
|
+
description: 'Compare multiple strategies by success rate',
|
|
97
|
+
inputSchema: {
|
|
98
|
+
type: 'object',
|
|
99
|
+
properties: { taskType: { type: 'string' } },
|
|
100
|
+
required: ['taskType'],
|
|
101
|
+
},
|
|
102
|
+
async execute(input): Promise<MCPToolResult> {
|
|
103
|
+
const taskType = input.taskType as string;
|
|
104
|
+
const relevant = trajectories.filter(t => t.taskType === taskType && t.outcome);
|
|
105
|
+
const total = relevant.length;
|
|
106
|
+
const successes = relevant.filter(t => t.outcome === 'success').length;
|
|
107
|
+
const partials = relevant.filter(t => t.outcome === 'partial').length;
|
|
108
|
+
const failures = relevant.filter(t => t.outcome === 'failure').length;
|
|
109
|
+
return {
|
|
110
|
+
content: JSON.stringify({
|
|
111
|
+
taskType, total, successRate: total ? successes / total : 0,
|
|
112
|
+
breakdown: { successes, partials, failures },
|
|
113
|
+
}),
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const rlGenerateTrainingData: MCPTool = {
|
|
119
|
+
name: 'rl_generate_training_data',
|
|
120
|
+
description: 'Export trajectories as fine-tuning JSONL',
|
|
121
|
+
inputSchema: {
|
|
122
|
+
type: 'object',
|
|
123
|
+
properties: { taskType: { type: 'string' }, minReward: { type: 'number' } },
|
|
124
|
+
},
|
|
125
|
+
async execute(input): Promise<MCPToolResult> {
|
|
126
|
+
let data = trajectories;
|
|
127
|
+
if (input.taskType) data = data.filter(t => t.taskType === input.taskType);
|
|
128
|
+
if (input.minReward != null) data = data.filter(t => t.totalReward >= (input.minReward as number));
|
|
129
|
+
const jsonl = data.map(t => JSON.stringify({
|
|
130
|
+
messages: [
|
|
131
|
+
{ role: 'system', content: `Task: ${t.taskType}` },
|
|
132
|
+
...t.actions.map(a => ({ role: 'assistant', content: a.action })),
|
|
133
|
+
],
|
|
134
|
+
outcome: t.outcome,
|
|
135
|
+
reward: t.totalReward,
|
|
136
|
+
})).join('\n');
|
|
137
|
+
return { content: jsonl || '(no data)' };
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const rlRewardSignal: MCPTool = {
|
|
142
|
+
name: 'rl_reward_signal',
|
|
143
|
+
description: 'Record positive/negative reward for last action',
|
|
144
|
+
inputSchema: {
|
|
145
|
+
type: 'object',
|
|
146
|
+
properties: { reward: { type: 'number' }, reason: { type: 'string' } },
|
|
147
|
+
required: ['reward'],
|
|
148
|
+
},
|
|
149
|
+
async execute(input): Promise<MCPToolResult> {
|
|
150
|
+
if (!currentEpisode || currentEpisode.actions.length === 0) {
|
|
151
|
+
return { content: 'No current episode or actions to reward', isError: true };
|
|
152
|
+
}
|
|
153
|
+
const lastAction = currentEpisode.actions[currentEpisode.actions.length - 1];
|
|
154
|
+
lastAction.reward = input.reward as number;
|
|
155
|
+
currentEpisode.totalReward += input.reward as number;
|
|
156
|
+
return { content: JSON.stringify({ action: lastAction.action, reward: input.reward, totalReward: currentEpisode.totalReward }) };
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export const rlExplorationSuggest: MCPTool = {
|
|
161
|
+
name: 'rl_exploration_suggest',
|
|
162
|
+
description: 'Suggest alternative approaches (exploration)',
|
|
163
|
+
inputSchema: {
|
|
164
|
+
type: 'object',
|
|
165
|
+
properties: { taskType: { type: 'string' }, currentAction: { type: 'string' } },
|
|
166
|
+
required: ['taskType'],
|
|
167
|
+
},
|
|
168
|
+
async execute(input): Promise<MCPToolResult> {
|
|
169
|
+
const taskType = input.taskType as string;
|
|
170
|
+
const allActions = new Set<string>();
|
|
171
|
+
trajectories.filter(t => t.taskType === taskType).forEach(t => t.actions.forEach(a => allActions.add(a.action)));
|
|
172
|
+
const suggestions = Array.from(allActions).filter(a => a !== input.currentAction).slice(0, 5);
|
|
173
|
+
if (suggestions.length === 0) {
|
|
174
|
+
return { content: JSON.stringify({ suggestions: [], message: 'No alternative actions found. Try a completely new approach.' }) };
|
|
175
|
+
}
|
|
176
|
+
return { content: JSON.stringify({ suggestions }) };
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export const rlUpdatePolicy: MCPTool = {
|
|
181
|
+
name: 'rl_update_policy',
|
|
182
|
+
description: "Update agent's action preferences based on rewards",
|
|
183
|
+
inputSchema: {
|
|
184
|
+
type: 'object',
|
|
185
|
+
properties: { taskType: { type: 'string' }, action: { type: 'string' }, weight: { type: 'number' } },
|
|
186
|
+
required: ['taskType', 'action', 'weight'],
|
|
187
|
+
},
|
|
188
|
+
async execute(input): Promise<MCPToolResult> {
|
|
189
|
+
const taskType = input.taskType as string;
|
|
190
|
+
let policy = policies.get(taskType);
|
|
191
|
+
if (!policy) {
|
|
192
|
+
policy = { taskType, preferredActions: [], weights: {} };
|
|
193
|
+
policies.set(taskType, policy);
|
|
194
|
+
}
|
|
195
|
+
const action = input.action as string;
|
|
196
|
+
policy.weights[action] = (policy.weights[action] || 0) + (input.weight as number);
|
|
197
|
+
policy.preferredActions = Object.entries(policy.weights)
|
|
198
|
+
.sort(([, a], [, b]) => b - a)
|
|
199
|
+
.map(([k]) => k);
|
|
200
|
+
return { content: JSON.stringify({ taskType, preferredActions: policy.preferredActions.slice(0, 5), weights: policy.weights }) };
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export const rlGetStatistics: MCPTool = {
|
|
205
|
+
name: 'rl_get_statistics',
|
|
206
|
+
description: 'Get success/failure stats by task type',
|
|
207
|
+
inputSchema: {
|
|
208
|
+
type: 'object',
|
|
209
|
+
properties: { taskType: { type: 'string' } },
|
|
210
|
+
},
|
|
211
|
+
async execute(input): Promise<MCPToolResult> {
|
|
212
|
+
let data = trajectories;
|
|
213
|
+
if (input.taskType) data = data.filter(t => t.taskType === input.taskType);
|
|
214
|
+
const stats: Record<string, { total: number; success: number; partial: number; failure: number; avgReward: number }> = {};
|
|
215
|
+
for (const t of data) {
|
|
216
|
+
if (!stats[t.taskType]) stats[t.taskType] = { total: 0, success: 0, partial: 0, failure: 0, avgReward: 0 };
|
|
217
|
+
const s = stats[t.taskType];
|
|
218
|
+
s.total++;
|
|
219
|
+
if (t.outcome === 'success') s.success++;
|
|
220
|
+
else if (t.outcome === 'partial') s.partial++;
|
|
221
|
+
else if (t.outcome === 'failure') s.failure++;
|
|
222
|
+
s.avgReward = (s.avgReward * (s.total - 1) + t.totalReward) / s.total;
|
|
223
|
+
}
|
|
224
|
+
return { content: JSON.stringify(stats) };
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
export const rlResetEpisode: MCPTool = {
|
|
229
|
+
name: 'rl_reset_episode',
|
|
230
|
+
description: 'Clear current episode state',
|
|
231
|
+
inputSchema: { type: 'object', properties: {} },
|
|
232
|
+
async execute(): Promise<MCPToolResult> {
|
|
233
|
+
const had = currentEpisode != null;
|
|
234
|
+
currentEpisode = null;
|
|
235
|
+
return { content: JSON.stringify({ reset: true, hadActiveEpisode: had }) };
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
export const rlTools: MCPTool[] = [
|
|
240
|
+
rlRecordTrajectory, rlEvaluateOutcome, rlGetBestStrategy, rlCompareStrategies,
|
|
241
|
+
rlGenerateTrainingData, rlRewardSignal, rlExplorationSuggest, rlUpdatePolicy,
|
|
242
|
+
rlGetStatistics, rlResetEpisode,
|
|
243
|
+
];
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import type { MCPTool, MCPToolResult } from '../mcp';
|
|
4
|
-
|
|
5
|
-
export const shellTool: MCPTool = {
|
|
6
|
-
name: 'shell_exec',
|
|
7
|
-
description: 'Execute a shell command (sandboxed to workspace)',
|
|
8
|
-
inputSchema: {
|
|
9
|
-
type: 'object',
|
|
10
|
-
properties: {
|
|
11
|
-
command: { type: 'string' },
|
|
12
|
-
timeout: { type: 'number', default: 30000 },
|
|
13
|
-
},
|
|
14
|
-
required: ['command'],
|
|
15
|
-
},
|
|
16
|
-
async execute(input: Record<string, unknown>): Promise<MCPToolResult> {
|
|
17
|
-
const command = input.command as string;
|
|
18
|
-
const timeout = (input.timeout as number) || 30000;
|
|
19
|
-
const workspace = process.cwd();
|
|
20
|
-
|
|
21
|
-
// Block path traversal attempts
|
|
22
|
-
if (command.includes('..')) {
|
|
23
|
-
return { content: 'Commands with ".." are not allowed for security', isError: true };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
const output = execSync(command, {
|
|
28
|
-
cwd: workspace,
|
|
29
|
-
timeout,
|
|
30
|
-
encoding: 'utf-8',
|
|
31
|
-
maxBuffer: 1024 * 1024,
|
|
32
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
33
|
-
});
|
|
34
|
-
const result = (output || '').slice(0, 5000);
|
|
35
|
-
return { content: result || '(no output)', isError: false };
|
|
36
|
-
} catch (err: any) {
|
|
37
|
-
const stderr = err.stderr ? String(err.stderr).slice(0, 2500) : '';
|
|
38
|
-
const stdout = err.stdout ? String(err.stdout).slice(0, 2500) : '';
|
|
39
|
-
const output = [stdout, stderr].filter(Boolean).join('\n') || err.message;
|
|
40
|
-
return { content: `Command failed: ${output.slice(0, 5000)}`, isError: true };
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
};
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import type { MCPTool, MCPToolResult } from '../mcp';
|
|
4
|
+
|
|
5
|
+
export const shellTool: MCPTool = {
|
|
6
|
+
name: 'shell_exec',
|
|
7
|
+
description: 'Execute a shell command (sandboxed to workspace)',
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
command: { type: 'string' },
|
|
12
|
+
timeout: { type: 'number', default: 30000 },
|
|
13
|
+
},
|
|
14
|
+
required: ['command'],
|
|
15
|
+
},
|
|
16
|
+
async execute(input: Record<string, unknown>): Promise<MCPToolResult> {
|
|
17
|
+
const command = input.command as string;
|
|
18
|
+
const timeout = (input.timeout as number) || 30000;
|
|
19
|
+
const workspace = process.cwd();
|
|
20
|
+
|
|
21
|
+
// Block path traversal attempts
|
|
22
|
+
if (command.includes('..')) {
|
|
23
|
+
return { content: 'Commands with ".." are not allowed for security', isError: true };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const output = execSync(command, {
|
|
28
|
+
cwd: workspace,
|
|
29
|
+
timeout,
|
|
30
|
+
encoding: 'utf-8',
|
|
31
|
+
maxBuffer: 1024 * 1024,
|
|
32
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
33
|
+
});
|
|
34
|
+
const result = (output || '').slice(0, 5000);
|
|
35
|
+
return { content: result || '(no output)', isError: false };
|
|
36
|
+
} catch (err: any) {
|
|
37
|
+
const stderr = err.stderr ? String(err.stderr).slice(0, 2500) : '';
|
|
38
|
+
const stdout = err.stdout ? String(err.stdout).slice(0, 2500) : '';
|
|
39
|
+
const output = [stdout, stderr].filter(Boolean).join('\n') || err.message;
|
|
40
|
+
return { content: `Command failed: ${output.slice(0, 5000)}`, isError: true };
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
};
|