opc-agent 1.3.1 → 1.4.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.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Bug Report
3
+ about: Report a bug
4
+ labels: bug
5
+ ---
6
+
7
+ **Describe the bug**
8
+ A clear description of the bug.
9
+
10
+ **To Reproduce**
11
+ Steps to reproduce:
12
+ 1. ...
13
+
14
+ **Expected behavior**
15
+ What you expected to happen.
16
+
17
+ **Environment**
18
+ - OS:
19
+ - Node.js:
20
+ - Package version:
@@ -0,0 +1,14 @@
1
+ ---
2
+ name: Feature Request
3
+ about: Suggest a new feature
4
+ labels: enhancement
5
+ ---
6
+
7
+ **Problem**
8
+ What problem does this solve?
9
+
10
+ **Proposed Solution**
11
+ How should it work?
12
+
13
+ **Alternatives Considered**
14
+ Any other approaches?
@@ -0,0 +1,13 @@
1
+ ## What
2
+ Brief description of changes.
3
+
4
+ ## Why
5
+ Why is this change needed?
6
+
7
+ ## How
8
+ How was this implemented?
9
+
10
+ ## Checklist
11
+ - [ ] Tests pass
12
+ - [ ] Documentation updated (if needed)
13
+ - [ ] No breaking changes (or documented)
package/CHANGELOG.md CHANGED
@@ -1,57 +1,23 @@
1
- # Changelog
2
-
3
- All notable changes to OPC Agent will be documented in this file.
4
-
5
- ## [1.2.0] - 2026-04-17
6
-
7
- ### Added
8
- - **Tool Gateway**: Managed tool gateway (`ToolGateway`) that wraps multiple tool providers (web-search, image-gen, tts, browser) behind a single authenticated endpoint — no separate API keys needed. Auto-discovers available tools from gateway, falls back to defaults. Gateway tools implement `MCPTool` interface for seamless registry integration. (`src/tools/gateway.ts`)
9
- - **Streaming Support**: SSE-based streaming for real-time agent responses. `StreamingManager` with `createStream()` / `writeChunk()` / `endStream()`. `StreamableResponse` with backpressure handling, event emission, and SSE pipe utility for Express-style HTTP responses. Channels can opt in via `supportsStreaming: true`. (`src/core/streaming.ts`)
10
-
11
- ## [1.1.0] - 2026-04-16
12
-
13
- ### Added
14
- - **Feishu/Lark Channel**: Full Feishu bot integration with event subscription webhook, tenant access token caching, text & interactive card messaging, group + P2P support. Also works with Lark international via `apiBase` config. (`src/channels/feishu.ts`)
15
- - **Discord Channel**: Discord bot via Gateway WebSocket with auto-reconnect, heartbeat, message content intent, and 2000-char message splitting. (`src/channels/discord.ts`)
16
- - **ProcessWatcher**: Background process output monitoring with regex pattern matching — watch stdout/stderr for specific patterns (errors, "server ready", build completion) and get instant callbacks without polling. Supports `once` patterns, match history, and dynamic pattern add/remove. Inspired by Hermes Agent's `watch_patterns`. (`src/core/watch.ts`)
17
-
18
- ## [0.2.0] - 2026-04-15
19
-
20
- ### Added
21
- - **DeepBrain Integration**: Optional long-term memory backend with semantic search. Config via `memory.longTerm.provider: deepbrain`. Falls back to in-memory if not installed.
22
- - **Telegram Channel**: Basic webhook handler for Telegram Bot API (`src/channels/telegram.ts`).
23
- - **WebSocket Channel**: Real-time bidirectional communication with broadcast (`src/channels/websocket.ts`).
24
- - **Sales Assistant Template**: Product Q&A, lead capture, appointment booking.
25
- - **Knowledge Base Template**: RAG with DeepBrain for answering from company docs.
26
- - **Code Reviewer Template**: Bug detection, style checking, severity ratings.
27
- - **Skill Marketplace Stub**: `opc publish` validates OAD and generates manifest. `opc search` placeholder.
28
- - **OAD Marketplace Fields**: `pricing` (free/freemium/paid/enterprise) and `tags` in marketplace config.
29
- - **Context Size Guard**: Auto-truncate tool outputs exceeding 5000 characters.
30
- - **Conversation History Limit**: Configurable limit (default 50 messages).
31
- - **Graceful Shutdown**: Handles SIGINT/SIGTERM with cleanup.
32
- - **Structured Logging**: Logger with debug/info/warn/error levels.
33
- - **Interactive CLI Init**: `opc init` with prompts and template selection.
34
- - **Dev Mode**: `opc dev` watches files and hot-reloads agent on changes.
35
- - **Agent Info Command**: `opc info` displays agent details from OAD.
36
- - **Colorful CLI Output**: Status indicators and colored text throughout CLI.
37
- - **CONTRIBUTING.md**: Contribution guidelines.
38
- - **CHANGELOG.md**: This file.
39
-
40
- ### Changed
41
- - OAD `memory.longTerm` now accepts boolean or object with provider config.
42
- - OAD `channels.type` now includes `telegram`.
43
- - CLI version bumped to 0.2.0.
44
-
45
- ## [0.1.0] - 2026-04-14
46
-
47
- ### Added
48
- - Initial release
49
- - BaseAgent with lifecycle management
50
- - AgentRuntime for config-driven setup
51
- - OAD Schema v1 with Zod validation
52
- - Web channel (Express)
53
- - InMemoryStore
54
- - Skill system (BaseSkill, SkillRegistry)
55
- - DTV framework (Trust, Value, Data)
56
- - Customer Service template
57
- - CLI: init, create, build, test, run, publish
1
+ # Changelog
2
+
3
+ ## 1.4.0 (2026-04-18)
4
+ - feat: wire Analytics into AgentRuntime (message timing, skill usage, error tracking)
5
+ - feat: expose analytics snapshot on /health and /api/dashboard endpoints
6
+ - feat: enhanced /health endpoint with agent name, version, uptime, memory type, skills, channels
7
+ - feat: Slack channel — real Events API webhook server + chat.postMessage via fetch
8
+ - feat: WebChannel metadata setters (version, memory type, skills, channels, analytics provider)
9
+ - feat: AgentRuntime.getAnalytics() and getConfig() accessors
10
+
11
+ ## 1.3.1 (2026-04-17)
12
+ - fix: remove residual DTV/marketplace references
13
+ - fix: duplicate WatchPattern export
14
+
15
+ ## 1.3.0 (2026-04-17)
16
+ - feat: Traces collection (OpenTelemetry-style)
17
+ - feat: DeepBrain exporter
18
+ - feat: brain/logs/score CLI commands
19
+
20
+ ## 1.2.0
21
+ - Initial public release
22
+ - 11 channels, plugins, analytics
23
+ - Declarative OAD configuration
package/CONTRIBUTING.md CHANGED
@@ -1,75 +1,36 @@
1
- # Contributing to OPC Agent
2
-
3
- Thank you for your interest in contributing to OPC Agent! 🎉
4
-
5
- ## Getting Started
6
-
7
- 1. Fork the repository
8
- 2. Clone your fork: `git clone https://github.com/YOUR_USERNAME/opc-agent.git`
9
- 3. Install dependencies: `npm install`
10
- 4. Create a branch: `git checkout -b feature/my-feature`
11
-
12
- ## Development
13
-
14
- ```bash
15
- # Build
16
- npm run build
17
-
18
- # Run tests
19
- npm test
20
-
21
- # Type check
22
- npm run lint
23
-
24
- # Watch mode
25
- npm run dev
26
- ```
27
-
28
- ## Project Structure
29
-
30
- ```
31
- src/
32
- ├── core/ # Agent, Runtime, Config, Logger, Types
33
- ├── channels/ # Web, WebSocket, Telegram channels
34
- ├── memory/ # InMemoryStore, DeepBrainMemoryStore
35
- ├── providers/ # LLM provider abstraction
36
- ├── schema/ # OAD schema (Zod)
37
- ├── skills/ # BaseSkill, SkillRegistry
38
- ├── templates/ # Agent templates
39
- ├── dtv/ # Data, Trust, Value framework
40
- ├── cli.ts # CLI entry point
41
- └── index.ts # Public API exports
42
- ```
43
-
44
- ## Guidelines
45
-
46
- - **TypeScript**: All code must be TypeScript with strict mode
47
- - **Tests**: Add tests for new features in `tests/`
48
- - **Commits**: Use conventional commits (`feat:`, `fix:`, `docs:`, etc.)
49
- - **OAD Compatibility**: Changes to OAD schema must be backward-compatible
50
- - **No Breaking Changes** without major version bump
51
-
52
- ## Adding a Template
53
-
54
- 1. Create `src/templates/my-template.ts` with config factory function
55
- 2. Create `templates/my-template/oad.yaml` and `README.md`
56
- 3. Register in `src/cli.ts` TEMPLATES map
57
- 4. Add tests
58
-
59
- ## Adding a Channel
60
-
61
- 1. Create `src/channels/my-channel.ts` extending `BaseChannel`
62
- 2. Add channel type to OAD schema in `src/schema/oad.ts`
63
- 3. Wire up in `src/core/runtime.ts`
64
- 4. Export from `src/index.ts`
65
-
66
- ## Pull Requests
67
-
68
- 1. Ensure all tests pass: `npm test`
69
- 2. Ensure types check: `npm run lint`
70
- 3. Write a clear PR description
71
- 4. Reference any related issues
72
-
73
- ## License
74
-
75
- By contributing, you agree that your contributions will be licensed under Apache-2.0.
1
+ # Contributing
2
+
3
+ Thanks for your interest in contributing!
4
+
5
+ ## Getting Started
6
+
7
+ 1. Fork the repo
8
+ 2. Clone your fork
9
+ 3. Install dependencies: `npm install`
10
+ 4. Create a branch: `git checkout -b feat/my-feature`
11
+ 5. Make your changes
12
+ 6. Run tests: `npm test`
13
+ 7. Commit: `git commit -m "feat: description"`
14
+ 8. Push and open a PR
15
+
16
+ ## Commit Convention
17
+
18
+ - `feat:` New feature
19
+ - `fix:` Bug fix
20
+ - `docs:` Documentation
21
+ - `test:` Tests
22
+ - `refactor:` Refactoring
23
+ - `chore:` Build/tooling
24
+
25
+ ## Code Style
26
+
27
+ - TypeScript
28
+ - Run `npm run lint` before committing (if available)
29
+
30
+ ## Questions?
31
+
32
+ Open a Discussion or Issue.
33
+
34
+ ## License
35
+
36
+ By contributing, you agree that your contributions will be licensed under Apache-2.0.
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.SlackChannel = void 0;
4
37
  const index_1 = require("./index");
@@ -37,9 +70,51 @@ class SlackChannel extends index_1.BaseChannel {
37
70
  }
38
71
  /** Start Events API HTTP server */
39
72
  async startEventsAPI() {
40
- // TODO: Implement with express or http
41
- // const port = this.config.port ?? 3001;
42
- // Listen for POST /slack/events and /slack/commands
73
+ const http = await Promise.resolve().then(() => __importStar(require('http')));
74
+ const port = this.config.port ?? 3001;
75
+ const server = http.createServer(async (req, res) => {
76
+ if (req.method !== 'POST') {
77
+ res.writeHead(404);
78
+ res.end();
79
+ return;
80
+ }
81
+ const chunks = [];
82
+ for await (const chunk of req)
83
+ chunks.push(chunk);
84
+ const body = JSON.parse(Buffer.concat(chunks).toString());
85
+ // URL verification challenge
86
+ if (body.type === 'url_verification') {
87
+ res.writeHead(200, { 'Content-Type': 'application/json' });
88
+ res.end(JSON.stringify({ challenge: body.challenge }));
89
+ return;
90
+ }
91
+ // Event callback
92
+ if (body.type === 'event_callback' && body.event) {
93
+ const event = body.event;
94
+ if (event.type === 'message' || event.type === 'app_mention') {
95
+ // Don't block the HTTP response
96
+ this.handleMessage(event).catch(() => { });
97
+ }
98
+ }
99
+ // Slash commands (form-urlencoded, but we handle JSON for simplicity)
100
+ if (req.url === '/slack/commands' && body.command) {
101
+ const reply = await this.handleSlashCommand({
102
+ command: body.command,
103
+ text: body.text ?? '',
104
+ userId: body.user_id,
105
+ channelId: body.channel_id,
106
+ triggerId: body.trigger_id,
107
+ });
108
+ res.writeHead(200, { 'Content-Type': 'application/json' });
109
+ res.end(JSON.stringify({ response_type: 'ephemeral', text: reply }));
110
+ return;
111
+ }
112
+ res.writeHead(200);
113
+ res.end('ok');
114
+ });
115
+ server.listen(port, () => {
116
+ console.log(`[SlackChannel] Events API listening on port ${port}`);
117
+ });
43
118
  }
44
119
  /** Handle incoming Slack message */
45
120
  async handleMessage(event) {
@@ -94,13 +169,21 @@ class SlackChannel extends index_1.BaseChannel {
94
169
  }
95
170
  /** Send a message to a Slack channel */
96
171
  async sendMessage(channel, text, threadTs) {
97
- // TODO: Implement with @slack/web-api
98
- // const { WebClient } = await import('@slack/web-api');
99
- // const client = new WebClient(this.config.botToken);
100
- // await client.chat.postMessage({ channel, text, thread_ts: threadTs });
101
- void channel;
102
- void text;
103
- void threadTs;
172
+ const body = { channel, text };
173
+ if (threadTs)
174
+ body.thread_ts = threadTs;
175
+ const res = await fetch('https://slack.com/api/chat.postMessage', {
176
+ method: 'POST',
177
+ headers: {
178
+ 'Authorization': `Bearer ${this.config.botToken}`,
179
+ 'Content-Type': 'application/json',
180
+ },
181
+ body: JSON.stringify(body),
182
+ });
183
+ const data = await res.json();
184
+ if (!data.ok) {
185
+ console.error(`[SlackChannel] chat.postMessage failed: ${data.error}`);
186
+ }
104
187
  }
105
188
  }
106
189
  exports.SlackChannel = SlackChannel;
@@ -9,6 +9,11 @@ export declare class WebChannel extends BaseChannel {
9
9
  private port;
10
10
  private streamHandler;
11
11
  private agentName;
12
+ private agentVersion;
13
+ private memoryType;
14
+ private skillNames;
15
+ private channelNames;
16
+ private analyticsProvider;
12
17
  private currentProvider;
13
18
  private stats;
14
19
  private eventHandlers;
@@ -23,6 +28,11 @@ export declare class WebChannel extends BaseChannel {
23
28
  trackSession(): void;
24
29
  constructor(port?: number, authConfig?: AuthConfig);
25
30
  setAgentName(name: string): void;
31
+ setAgentVersion(version: string): void;
32
+ setMemoryType(type: string): void;
33
+ setSkillNames(names: string[]): void;
34
+ setChannelNames(names: string[]): void;
35
+ setAnalyticsProvider(fn: () => any): void;
26
36
  onStreamMessage(handler: (msg: Message, res: Response) => Promise<void>): void;
27
37
  private setupRoutes;
28
38
  start(): Promise<void>;
@@ -286,6 +286,11 @@ class WebChannel extends index_1.BaseChannel {
286
286
  port;
287
287
  streamHandler = null;
288
288
  agentName = 'OPC Agent';
289
+ agentVersion = '1.0.0';
290
+ memoryType = 'in-memory';
291
+ skillNames = [];
292
+ channelNames = ['web'];
293
+ analyticsProvider = null;
289
294
  currentProvider = 'openai';
290
295
  stats = { sessions: 0, messages: 0, totalResponseMs: 0, tokenUsage: 0, knowledgeFiles: 0, startedAt: Date.now(), errors: 0 };
291
296
  eventHandlers = new Map();
@@ -326,6 +331,21 @@ class WebChannel extends index_1.BaseChannel {
326
331
  setAgentName(name) {
327
332
  this.agentName = name;
328
333
  }
334
+ setAgentVersion(version) {
335
+ this.agentVersion = version;
336
+ }
337
+ setMemoryType(type) {
338
+ this.memoryType = type;
339
+ }
340
+ setSkillNames(names) {
341
+ this.skillNames = names;
342
+ }
343
+ setChannelNames(names) {
344
+ this.channelNames = names;
345
+ }
346
+ setAnalyticsProvider(fn) {
347
+ this.analyticsProvider = fn;
348
+ }
329
349
  onStreamMessage(handler) {
330
350
  this.streamHandler = handler;
331
351
  }
@@ -334,7 +354,17 @@ class WebChannel extends index_1.BaseChannel {
334
354
  res.type('html').send(CHAT_HTML);
335
355
  });
336
356
  this.app.get('/health', (_req, res) => {
337
- res.json({ status: 'ok', timestamp: Date.now() });
357
+ res.json({
358
+ status: 'ok',
359
+ agent: this.agentName,
360
+ version: this.agentVersion,
361
+ uptime: Date.now() - this.stats.startedAt,
362
+ memory: this.memoryType,
363
+ skills: this.skillNames,
364
+ channels: this.channelNames,
365
+ analytics: this.analyticsProvider ? this.analyticsProvider() : null,
366
+ timestamp: Date.now(),
367
+ });
338
368
  });
339
369
  this.app.get('/api/info', (_req, res) => {
340
370
  res.json({ name: this.agentName });
@@ -408,7 +438,8 @@ class WebChannel extends index_1.BaseChannel {
408
438
  res.type('html').send(DASHBOARD_HTML);
409
439
  });
410
440
  this.app.get('/api/dashboard', (_req, res) => {
411
- res.json(this.stats);
441
+ const analytics = this.analyticsProvider ? this.analyticsProvider() : null;
442
+ res.json({ ...this.stats, analytics });
412
443
  });
413
444
  // --- Knowledge Base Upload ---
414
445
  this.app.post('/api/kb/upload', async (req, res) => {