opc-agent 3.0.1 → 4.0.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.
- package/README.md +30 -24
- package/dist/channels/dingtalk.d.ts +17 -0
- package/dist/channels/dingtalk.js +38 -0
- package/dist/channels/googlechat.d.ts +14 -0
- package/dist/channels/googlechat.js +37 -0
- package/dist/channels/imessage.d.ts +13 -0
- package/dist/channels/imessage.js +28 -0
- package/dist/channels/irc.d.ts +20 -0
- package/dist/channels/irc.js +71 -0
- package/dist/channels/line.d.ts +14 -0
- package/dist/channels/line.js +28 -0
- package/dist/channels/matrix.d.ts +15 -0
- package/dist/channels/matrix.js +28 -0
- package/dist/channels/mattermost.d.ts +18 -0
- package/dist/channels/mattermost.js +49 -0
- package/dist/channels/msteams.d.ts +14 -0
- package/dist/channels/msteams.js +28 -0
- package/dist/channels/nostr.d.ts +14 -0
- package/dist/channels/nostr.js +28 -0
- package/dist/channels/qq.d.ts +15 -0
- package/dist/channels/qq.js +28 -0
- package/dist/channels/signal.d.ts +14 -0
- package/dist/channels/signal.js +28 -0
- package/dist/channels/sms.d.ts +15 -0
- package/dist/channels/sms.js +28 -0
- package/dist/channels/twitch.d.ts +17 -0
- package/dist/channels/twitch.js +59 -0
- package/dist/channels/voice-call.d.ts +27 -0
- package/dist/channels/voice-call.js +82 -0
- package/dist/channels/whatsapp.d.ts +14 -0
- package/dist/channels/whatsapp.js +28 -0
- package/dist/cli.js +36 -0
- package/dist/core/api-server.d.ts +25 -0
- package/dist/core/api-server.js +286 -0
- package/dist/core/audio.d.ts +50 -0
- package/dist/core/audio.js +68 -0
- package/dist/core/context-discovery.d.ts +16 -0
- package/dist/core/context-discovery.js +107 -0
- package/dist/core/context-refs.d.ts +29 -0
- package/dist/core/context-refs.js +162 -0
- package/dist/core/gateway.d.ts +53 -0
- package/dist/core/gateway.js +80 -0
- package/dist/core/heartbeat.d.ts +19 -0
- package/dist/core/heartbeat.js +50 -0
- package/dist/core/hooks.d.ts +28 -0
- package/dist/core/hooks.js +82 -0
- package/dist/core/ide-bridge.d.ts +53 -0
- package/dist/core/ide-bridge.js +97 -0
- package/dist/core/node-network.d.ts +23 -0
- package/dist/core/node-network.js +77 -0
- package/dist/core/profiles.d.ts +27 -0
- package/dist/core/profiles.js +131 -0
- package/dist/core/sandbox.d.ts +25 -0
- package/dist/core/sandbox.js +84 -1
- package/dist/core/session-manager.d.ts +33 -0
- package/dist/core/session-manager.js +157 -0
- package/dist/core/vision.d.ts +45 -0
- package/dist/core/vision.js +177 -0
- package/dist/index.d.ts +64 -1
- package/dist/index.js +86 -3
- package/dist/memory/context-compressor.d.ts +43 -0
- package/dist/memory/context-compressor.js +167 -0
- package/dist/memory/index.d.ts +4 -0
- package/dist/memory/index.js +5 -1
- package/dist/memory/user-profiler.d.ts +50 -0
- package/dist/memory/user-profiler.js +201 -0
- package/dist/schema/oad.d.ts +12 -12
- package/dist/security/approvals.d.ts +53 -0
- package/dist/security/approvals.js +115 -0
- package/dist/security/elevated.d.ts +41 -0
- package/dist/security/elevated.js +89 -0
- package/dist/security/index.d.ts +6 -0
- package/dist/security/index.js +7 -1
- package/dist/security/secrets.d.ts +34 -0
- package/dist/security/secrets.js +115 -0
- package/dist/tools/builtin/browser.d.ts +47 -0
- package/dist/tools/builtin/browser.js +284 -0
- package/dist/tools/builtin/home-assistant.d.ts +12 -0
- package/dist/tools/builtin/home-assistant.js +126 -0
- package/dist/tools/builtin/index.d.ts +6 -1
- package/dist/tools/builtin/index.js +18 -2
- package/dist/tools/builtin/rl-tools.d.ts +13 -0
- package/dist/tools/builtin/rl-tools.js +228 -0
- package/dist/tools/builtin/vision.d.ts +6 -0
- package/dist/tools/builtin/vision.js +61 -0
- package/package.json +2 -2
- package/src/channels/dingtalk.ts +46 -0
- package/src/channels/googlechat.ts +42 -0
- package/src/channels/imessage.ts +32 -0
- package/src/channels/irc.ts +82 -0
- package/src/channels/line.ts +33 -0
- package/src/channels/matrix.ts +34 -0
- package/src/channels/mattermost.ts +57 -0
- package/src/channels/msteams.ts +33 -0
- package/src/channels/nostr.ts +33 -0
- package/src/channels/qq.ts +34 -0
- package/src/channels/signal.ts +33 -0
- package/src/channels/sms.ts +34 -0
- package/src/channels/twitch.ts +65 -0
- package/src/channels/voice-call.ts +100 -0
- package/src/channels/whatsapp.ts +33 -0
- package/src/cli.ts +40 -0
- package/src/core/api-server.ts +277 -0
- package/src/core/audio.ts +98 -0
- package/src/core/context-discovery.ts +85 -0
- package/src/core/context-refs.ts +140 -0
- package/src/core/gateway.ts +106 -0
- package/src/core/heartbeat.ts +51 -0
- package/src/core/hooks.ts +105 -0
- package/src/core/ide-bridge.ts +133 -0
- package/src/core/node-network.ts +86 -0
- package/src/core/profiles.ts +122 -0
- package/src/core/sandbox.ts +100 -0
- package/src/core/session-manager.ts +137 -0
- package/src/core/vision.ts +180 -0
- package/src/index.ts +84 -1
- package/src/memory/context-compressor.ts +189 -0
- package/src/memory/index.ts +4 -0
- package/src/memory/user-profiler.ts +215 -0
- package/src/security/approvals.ts +143 -0
- package/src/security/elevated.ts +105 -0
- package/src/security/index.ts +6 -0
- package/src/security/secrets.ts +129 -0
- package/src/tools/builtin/browser.ts +299 -0
- package/src/tools/builtin/home-assistant.ts +116 -0
- package/src/tools/builtin/index.ts +9 -2
- package/src/tools/builtin/rl-tools.ts +243 -0
- package/src/tools/builtin/vision.ts +64 -0
- package/tests/api-server.test.ts +148 -0
- package/tests/approvals.test.ts +89 -0
- package/tests/audio.test.ts +40 -0
- package/tests/browser.test.ts +179 -0
- package/tests/builtin-tools.test.ts +83 -83
- package/tests/channels-extra.test.ts +45 -0
- package/tests/context-compressor.test.ts +172 -0
- package/tests/context-refs.test.ts +121 -0
- package/tests/elevated.test.ts +69 -0
- package/tests/gateway.test.ts +63 -71
- package/tests/home-assistant.test.ts +40 -0
- package/tests/hooks.test.ts +79 -0
- package/tests/ide-bridge.test.ts +38 -0
- package/tests/node-network.test.ts +74 -0
- package/tests/profiles.test.ts +61 -0
- package/tests/rl-tools.test.ts +93 -0
- package/tests/sandbox-manager.test.ts +46 -0
- package/tests/secrets.test.ts +107 -0
- package/tests/tools/builtin-extended.test.ts +138 -138
- package/tests/user-profiler.test.ts +169 -0
- package/tests/v090-features.test.ts +254 -0
- package/tests/vision.test.ts +61 -0
- package/tests/voice-call.test.ts +47 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ContextCompressor = void 0;
|
|
4
|
+
const DEFAULT_CONFIG = {
|
|
5
|
+
maxTokens: 8000,
|
|
6
|
+
compressThreshold: 0.8,
|
|
7
|
+
preserveRecent: 10,
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Context compression with optional DeepBrain memory offloading.
|
|
11
|
+
*/
|
|
12
|
+
class ContextCompressor {
|
|
13
|
+
config;
|
|
14
|
+
constructor(config = {}) {
|
|
15
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Estimate token count using language-aware heuristic.
|
|
19
|
+
* English: ~1 token per 4 chars. Chinese: ~1 token per 2 chars.
|
|
20
|
+
*/
|
|
21
|
+
estimateTokens(text) {
|
|
22
|
+
let tokens = 0;
|
|
23
|
+
for (const char of text) {
|
|
24
|
+
// CJK Unicode range detection
|
|
25
|
+
const code = char.codePointAt(0) ?? 0;
|
|
26
|
+
if ((code >= 0x4e00 && code <= 0x9fff) || // CJK Unified
|
|
27
|
+
(code >= 0x3400 && code <= 0x4dbf) || // CJK Extension A
|
|
28
|
+
(code >= 0x3000 && code <= 0x303f) // CJK Punctuation
|
|
29
|
+
) {
|
|
30
|
+
tokens += 0.5; // 1 token per 2 chars
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
tokens += 0.25; // 1 token per 4 chars
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return Math.ceil(tokens);
|
|
37
|
+
}
|
|
38
|
+
estimateMessagesTokens(messages) {
|
|
39
|
+
return messages.reduce((sum, m) => sum + this.estimateTokens(m.content), 0);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Extract key insights from messages for brain storage.
|
|
43
|
+
*/
|
|
44
|
+
extractInsights(messages) {
|
|
45
|
+
const insights = [];
|
|
46
|
+
for (const msg of messages) {
|
|
47
|
+
const c = msg.content;
|
|
48
|
+
// Decisions
|
|
49
|
+
if (/\b(decided|decision|choose|chose|will use|going with|let's go|确定|决定)\b/i.test(c)) {
|
|
50
|
+
insights.push({ content: c.slice(0, 500), type: 'decision' });
|
|
51
|
+
}
|
|
52
|
+
// Facts / definitions
|
|
53
|
+
else if (/\b(is defined as|means|equals|refers to|是指|定义)\b/i.test(c)) {
|
|
54
|
+
insights.push({ content: c.slice(0, 500), type: 'fact' });
|
|
55
|
+
}
|
|
56
|
+
// Preferences
|
|
57
|
+
else if (/\b(prefer|like|want|don't like|不喜欢|喜欢|偏好)\b/i.test(c)) {
|
|
58
|
+
insights.push({ content: c.slice(0, 500), type: 'preference' });
|
|
59
|
+
}
|
|
60
|
+
// Code snippets
|
|
61
|
+
else if (/```[\s\S]{20,}```/.test(c)) {
|
|
62
|
+
insights.push({ content: c.slice(0, 800), type: 'code' });
|
|
63
|
+
}
|
|
64
|
+
// Long assistant messages likely contain useful info
|
|
65
|
+
else if (msg.role === 'assistant' && c.length > 200) {
|
|
66
|
+
insights.push({ content: c.slice(0, 500), type: 'knowledge' });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return insights;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Generate a simple summary from messages (no-brain fallback).
|
|
73
|
+
*/
|
|
74
|
+
summarize(messages) {
|
|
75
|
+
const topics = new Set();
|
|
76
|
+
const keyLines = [];
|
|
77
|
+
for (const msg of messages) {
|
|
78
|
+
// Extract first meaningful sentence
|
|
79
|
+
const firstLine = msg.content.split(/[.\n!?。!?]/)[0]?.trim();
|
|
80
|
+
if (firstLine && firstLine.length > 10 && firstLine.length < 200) {
|
|
81
|
+
if (keyLines.length < 5)
|
|
82
|
+
keyLines.push(`[${msg.role}] ${firstLine}`);
|
|
83
|
+
}
|
|
84
|
+
// Extract topic words (capitalized words, Chinese phrases)
|
|
85
|
+
const words = msg.content.match(/[A-Z][a-z]{2,}/g) ?? [];
|
|
86
|
+
words.forEach(w => topics.add(w));
|
|
87
|
+
}
|
|
88
|
+
const topicStr = [...topics].slice(0, 10).join(', ');
|
|
89
|
+
const linesStr = keyLines.join('; ');
|
|
90
|
+
return `Topics: ${topicStr || 'general discussion'}. Key points: ${linesStr || 'varied conversation'}`;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Compress messages when token count exceeds threshold.
|
|
94
|
+
*/
|
|
95
|
+
async compress(messages, config) {
|
|
96
|
+
const cfg = { ...this.config, ...config };
|
|
97
|
+
const totalTokens = this.estimateMessagesTokens(messages);
|
|
98
|
+
const threshold = cfg.maxTokens * cfg.compressThreshold;
|
|
99
|
+
// Under threshold — return as-is
|
|
100
|
+
if (totalTokens <= threshold) {
|
|
101
|
+
return {
|
|
102
|
+
messages: [...messages],
|
|
103
|
+
learnedCount: 0,
|
|
104
|
+
savedTokens: 0,
|
|
105
|
+
summary: '',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const recentCount = Math.min(cfg.preserveRecent, messages.length);
|
|
109
|
+
const splitIdx = messages.length - recentCount;
|
|
110
|
+
const oldMessages = messages.slice(0, splitIdx);
|
|
111
|
+
const recentMessages = messages.slice(splitIdx);
|
|
112
|
+
if (oldMessages.length === 0) {
|
|
113
|
+
return { messages: [...messages], learnedCount: 0, savedTokens: 0, summary: '' };
|
|
114
|
+
}
|
|
115
|
+
const oldTokens = this.estimateMessagesTokens(oldMessages);
|
|
116
|
+
let learnedCount = 0;
|
|
117
|
+
let summary;
|
|
118
|
+
if (cfg.brain) {
|
|
119
|
+
// Extract and learn insights
|
|
120
|
+
const insights = this.extractInsights(oldMessages);
|
|
121
|
+
for (const insight of insights) {
|
|
122
|
+
try {
|
|
123
|
+
await cfg.brain.learn(insight.content, { insight_type: insight.type });
|
|
124
|
+
learnedCount++;
|
|
125
|
+
}
|
|
126
|
+
catch { /* non-critical */ }
|
|
127
|
+
}
|
|
128
|
+
summary = `${oldMessages.length} messages compressed. Extracted ${learnedCount} insights (${insights.map(i => i.type).filter((v, i, a) => a.indexOf(v) === i).join(', ')}).`;
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
summary = this.summarize(oldMessages);
|
|
132
|
+
}
|
|
133
|
+
const compressionMessage = {
|
|
134
|
+
id: `compressed-${Date.now()}`,
|
|
135
|
+
role: 'system',
|
|
136
|
+
content: `[Context compressed: ${oldMessages.length} messages → ${summary}${cfg.brain ? ' Details stored in Brain, use recall() to retrieve.' : ''}]`,
|
|
137
|
+
timestamp: Date.now(),
|
|
138
|
+
};
|
|
139
|
+
return {
|
|
140
|
+
messages: [compressionMessage, ...recentMessages],
|
|
141
|
+
learnedCount,
|
|
142
|
+
savedTokens: oldTokens - this.estimateTokens(compressionMessage.content),
|
|
143
|
+
summary,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Restore context from brain for a given query.
|
|
148
|
+
*/
|
|
149
|
+
async restore(query, brain) {
|
|
150
|
+
if (!brain?.recall)
|
|
151
|
+
return [];
|
|
152
|
+
try {
|
|
153
|
+
const results = await brain.recall(query);
|
|
154
|
+
if (Array.isArray(results)) {
|
|
155
|
+
return results.map((r) => typeof r === 'string' ? r : r.content ?? JSON.stringify(r));
|
|
156
|
+
}
|
|
157
|
+
if (typeof results === 'string')
|
|
158
|
+
return [results];
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
exports.ContextCompressor = ContextCompressor;
|
|
167
|
+
//# sourceMappingURL=context-compressor.js.map
|
package/dist/memory/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { Message, MemoryStore } from '../core/types';
|
|
2
2
|
export { BrainSeedLoader, KnowledgeEvolver } from './seed-loader';
|
|
3
3
|
export type { BrainSeedConfig, SeedPage, SeedResult, PromotionResult, PromotionCandidate } from './seed-loader';
|
|
4
|
+
export { ContextCompressor } from './context-compressor';
|
|
5
|
+
export type { CompressorConfig, CompressResult } from './context-compressor';
|
|
6
|
+
export { UserProfiler } from './user-profiler';
|
|
7
|
+
export type { UserProfile } from './user-profiler';
|
|
4
8
|
export declare class InMemoryStore implements MemoryStore {
|
|
5
9
|
private store;
|
|
6
10
|
private conversations;
|
package/dist/memory/index.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.InMemoryStore = exports.KnowledgeEvolver = exports.BrainSeedLoader = void 0;
|
|
3
|
+
exports.InMemoryStore = exports.UserProfiler = exports.ContextCompressor = exports.KnowledgeEvolver = exports.BrainSeedLoader = void 0;
|
|
4
4
|
var seed_loader_1 = require("./seed-loader");
|
|
5
5
|
Object.defineProperty(exports, "BrainSeedLoader", { enumerable: true, get: function () { return seed_loader_1.BrainSeedLoader; } });
|
|
6
6
|
Object.defineProperty(exports, "KnowledgeEvolver", { enumerable: true, get: function () { return seed_loader_1.KnowledgeEvolver; } });
|
|
7
|
+
var context_compressor_1 = require("./context-compressor");
|
|
8
|
+
Object.defineProperty(exports, "ContextCompressor", { enumerable: true, get: function () { return context_compressor_1.ContextCompressor; } });
|
|
9
|
+
var user_profiler_1 = require("./user-profiler");
|
|
10
|
+
Object.defineProperty(exports, "UserProfiler", { enumerable: true, get: function () { return user_profiler_1.UserProfiler; } });
|
|
7
11
|
class InMemoryStore {
|
|
8
12
|
store = new Map();
|
|
9
13
|
conversations = new Map();
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Message } from '../core/types';
|
|
2
|
+
export interface UserProfile {
|
|
3
|
+
preferences: Record<string, string>;
|
|
4
|
+
communication_style: string;
|
|
5
|
+
expertise_areas: string[];
|
|
6
|
+
common_requests: string[];
|
|
7
|
+
last_updated: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* DeepBrain-powered user profiling from conversation signals.
|
|
11
|
+
*/
|
|
12
|
+
export declare class UserProfiler {
|
|
13
|
+
private observationCount;
|
|
14
|
+
private signals;
|
|
15
|
+
private readonly LEARN_INTERVAL;
|
|
16
|
+
/**
|
|
17
|
+
* Detect language mix of a text.
|
|
18
|
+
*/
|
|
19
|
+
private detectLanguage;
|
|
20
|
+
/**
|
|
21
|
+
* Detect technical level from content.
|
|
22
|
+
*/
|
|
23
|
+
private detectTechLevel;
|
|
24
|
+
/**
|
|
25
|
+
* Detect communication style.
|
|
26
|
+
*/
|
|
27
|
+
private detectStyle;
|
|
28
|
+
/**
|
|
29
|
+
* Extract domain keywords.
|
|
30
|
+
*/
|
|
31
|
+
private extractDomainKeywords;
|
|
32
|
+
/**
|
|
33
|
+
* Classify request type.
|
|
34
|
+
*/
|
|
35
|
+
private classifyRequest;
|
|
36
|
+
/**
|
|
37
|
+
* Observe a user message and accumulate profile signals.
|
|
38
|
+
*/
|
|
39
|
+
observe(message: Message, brain?: any): Promise<void>;
|
|
40
|
+
private buildProfileFromSignals;
|
|
41
|
+
/**
|
|
42
|
+
* Get user profile, optionally from brain recall.
|
|
43
|
+
*/
|
|
44
|
+
getProfile(brain?: any): Promise<UserProfile>;
|
|
45
|
+
/**
|
|
46
|
+
* Enhance a system prompt with user profile context.
|
|
47
|
+
*/
|
|
48
|
+
enhance(systemPrompt: string, profile: UserProfile): string;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=user-profiler.d.ts.map
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UserProfiler = void 0;
|
|
4
|
+
const EMPTY_PROFILE = {
|
|
5
|
+
preferences: {},
|
|
6
|
+
communication_style: 'unknown',
|
|
7
|
+
expertise_areas: [],
|
|
8
|
+
common_requests: [],
|
|
9
|
+
last_updated: 0,
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* DeepBrain-powered user profiling from conversation signals.
|
|
13
|
+
*/
|
|
14
|
+
class UserProfiler {
|
|
15
|
+
observationCount = 0;
|
|
16
|
+
signals = {
|
|
17
|
+
languages: new Map(),
|
|
18
|
+
techKeywords: new Set(),
|
|
19
|
+
styleSignals: { brief: 0, detailed: 0, formal: 0, casual: 0 },
|
|
20
|
+
requestTypes: new Map(),
|
|
21
|
+
};
|
|
22
|
+
LEARN_INTERVAL = 20;
|
|
23
|
+
/**
|
|
24
|
+
* Detect language mix of a text.
|
|
25
|
+
*/
|
|
26
|
+
detectLanguage(text) {
|
|
27
|
+
let cn = 0, en = 0;
|
|
28
|
+
for (const char of text) {
|
|
29
|
+
const code = char.codePointAt(0) ?? 0;
|
|
30
|
+
if (code >= 0x4e00 && code <= 0x9fff)
|
|
31
|
+
cn++;
|
|
32
|
+
else if ((code >= 0x41 && code <= 0x5a) || (code >= 0x61 && code <= 0x7a))
|
|
33
|
+
en++;
|
|
34
|
+
}
|
|
35
|
+
if (cn > 0 && en > 0)
|
|
36
|
+
return 'mixed';
|
|
37
|
+
if (cn > en)
|
|
38
|
+
return 'chinese';
|
|
39
|
+
return 'english';
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Detect technical level from content.
|
|
43
|
+
*/
|
|
44
|
+
detectTechLevel(text) {
|
|
45
|
+
const expertTerms = /\b(kubernetes|k8s|microservice|architecture|distributed|consensus|raft|paxos|sharding|vector db|embedding|fine-?tun|transformer|CUDA|inference|quantiz|LoRA|RAG)\b/i;
|
|
46
|
+
const intermediateTerms = /\b(API|REST|GraphQL|Docker|CI\/CD|database|deploy|cloud|React|TypeScript|Python|async|cache|redis|nginx)\b/i;
|
|
47
|
+
if (expertTerms.test(text))
|
|
48
|
+
return 'expert';
|
|
49
|
+
if (intermediateTerms.test(text))
|
|
50
|
+
return 'intermediate';
|
|
51
|
+
return 'beginner';
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Detect communication style.
|
|
55
|
+
*/
|
|
56
|
+
detectStyle(text) {
|
|
57
|
+
if (text.length < 50)
|
|
58
|
+
this.signals.styleSignals.brief++;
|
|
59
|
+
else if (text.length > 300)
|
|
60
|
+
this.signals.styleSignals.detailed++;
|
|
61
|
+
if (/\b(please|kindly|would you|could you|请问|烦请|麻烦)\b/i.test(text)) {
|
|
62
|
+
this.signals.styleSignals.formal++;
|
|
63
|
+
}
|
|
64
|
+
if (/[!]{2,}|lol|haha|😂|🤣|哈哈|牛|666|👍/i.test(text)) {
|
|
65
|
+
this.signals.styleSignals.casual++;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Extract domain keywords.
|
|
70
|
+
*/
|
|
71
|
+
extractDomainKeywords(text) {
|
|
72
|
+
const techWords = text.match(/\b[A-Z][a-zA-Z]{2,}(?:\.js|\.ts|\.py)?\b/g) ?? [];
|
|
73
|
+
techWords.forEach(w => this.signals.techKeywords.add(w));
|
|
74
|
+
// Chinese tech terms
|
|
75
|
+
const cnTerms = text.match(/(?:人工智能|机器学习|深度学习|大模型|微服务|架构|部署|运维|前端|后端|数据库|缓存|分布式)/g) ?? [];
|
|
76
|
+
cnTerms.forEach(w => this.signals.techKeywords.add(w));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Classify request type.
|
|
80
|
+
*/
|
|
81
|
+
classifyRequest(text) {
|
|
82
|
+
if (/\b(how to|怎么|如何|how do)\b/i.test(text))
|
|
83
|
+
return 'how-to';
|
|
84
|
+
if (/\b(why|为什么|原因)\b/i.test(text))
|
|
85
|
+
return 'explanation';
|
|
86
|
+
if (/\b(fix|error|bug|报错|出错|failed)\b/i.test(text))
|
|
87
|
+
return 'debugging';
|
|
88
|
+
if (/\b(review|评审|看看|check)\b/i.test(text))
|
|
89
|
+
return 'review';
|
|
90
|
+
if (/\b(create|build|write|写|创建|生成)\b/i.test(text))
|
|
91
|
+
return 'creation';
|
|
92
|
+
return 'general';
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Observe a user message and accumulate profile signals.
|
|
96
|
+
*/
|
|
97
|
+
async observe(message, brain) {
|
|
98
|
+
if (message.role !== 'user')
|
|
99
|
+
return;
|
|
100
|
+
const text = message.content;
|
|
101
|
+
// Language
|
|
102
|
+
const lang = this.detectLanguage(text);
|
|
103
|
+
this.signals.languages.set(lang, (this.signals.languages.get(lang) ?? 0) + 1);
|
|
104
|
+
// Style
|
|
105
|
+
this.detectStyle(text);
|
|
106
|
+
// Domain keywords
|
|
107
|
+
this.extractDomainKeywords(text);
|
|
108
|
+
// Request type
|
|
109
|
+
const reqType = this.classifyRequest(text);
|
|
110
|
+
this.signals.requestTypes.set(reqType, (this.signals.requestTypes.get(reqType) ?? 0) + 1);
|
|
111
|
+
this.observationCount++;
|
|
112
|
+
// Periodically persist to brain
|
|
113
|
+
if (brain?.learn && this.observationCount % this.LEARN_INTERVAL === 0) {
|
|
114
|
+
const profile = this.buildProfileFromSignals();
|
|
115
|
+
try {
|
|
116
|
+
await brain.learn(JSON.stringify(profile), { insight_type: 'user_profile' });
|
|
117
|
+
}
|
|
118
|
+
catch { /* non-critical */ }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
buildProfileFromSignals() {
|
|
122
|
+
// Language preference
|
|
123
|
+
let topLang = 'english';
|
|
124
|
+
let maxCount = 0;
|
|
125
|
+
for (const [lang, count] of this.signals.languages) {
|
|
126
|
+
if (count > maxCount) {
|
|
127
|
+
topLang = lang;
|
|
128
|
+
maxCount = count;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Communication style
|
|
132
|
+
const ss = this.signals.styleSignals;
|
|
133
|
+
const styles = [
|
|
134
|
+
['brief', ss.brief], ['detailed', ss.detailed],
|
|
135
|
+
['formal', ss.formal], ['casual', ss.casual],
|
|
136
|
+
];
|
|
137
|
+
const topStyle = styles.reduce((a, b) => (b[1] > a[1] ? b : a))[0];
|
|
138
|
+
// Top request types
|
|
139
|
+
const topRequests = [...this.signals.requestTypes.entries()]
|
|
140
|
+
.sort((a, b) => b[1] - a[1])
|
|
141
|
+
.slice(0, 5)
|
|
142
|
+
.map(([t]) => t);
|
|
143
|
+
return {
|
|
144
|
+
preferences: { language: topLang },
|
|
145
|
+
communication_style: topStyle,
|
|
146
|
+
expertise_areas: [...this.signals.techKeywords].slice(0, 20),
|
|
147
|
+
common_requests: topRequests,
|
|
148
|
+
last_updated: Date.now(),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get user profile, optionally from brain recall.
|
|
153
|
+
*/
|
|
154
|
+
async getProfile(brain) {
|
|
155
|
+
// First try local signals
|
|
156
|
+
if (this.observationCount > 0) {
|
|
157
|
+
return this.buildProfileFromSignals();
|
|
158
|
+
}
|
|
159
|
+
// Fallback to brain recall
|
|
160
|
+
if (brain?.recall) {
|
|
161
|
+
try {
|
|
162
|
+
const results = await brain.recall('user profile preferences style');
|
|
163
|
+
if (Array.isArray(results) && results.length > 0) {
|
|
164
|
+
const raw = typeof results[0] === 'string' ? results[0] : results[0].content;
|
|
165
|
+
return { ...EMPTY_PROFILE, ...JSON.parse(raw) };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch { /* ignore parse errors */ }
|
|
169
|
+
}
|
|
170
|
+
return { ...EMPTY_PROFILE };
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Enhance a system prompt with user profile context.
|
|
174
|
+
*/
|
|
175
|
+
enhance(systemPrompt, profile) {
|
|
176
|
+
const hints = [];
|
|
177
|
+
if (profile.preferences.language) {
|
|
178
|
+
const langMap = {
|
|
179
|
+
chinese: 'User prefers Chinese responses.',
|
|
180
|
+
english: 'User prefers English responses.',
|
|
181
|
+
mixed: 'User uses mixed Chinese/English. Match their style.',
|
|
182
|
+
};
|
|
183
|
+
if (langMap[profile.preferences.language])
|
|
184
|
+
hints.push(langMap[profile.preferences.language]);
|
|
185
|
+
}
|
|
186
|
+
if (profile.communication_style && profile.communication_style !== 'unknown') {
|
|
187
|
+
hints.push(`User communication style: ${profile.communication_style}.`);
|
|
188
|
+
}
|
|
189
|
+
if (profile.expertise_areas.length > 0) {
|
|
190
|
+
hints.push(`User expertise: ${profile.expertise_areas.slice(0, 10).join(', ')}.`);
|
|
191
|
+
}
|
|
192
|
+
if (profile.common_requests.length > 0) {
|
|
193
|
+
hints.push(`Common request types: ${profile.common_requests.join(', ')}.`);
|
|
194
|
+
}
|
|
195
|
+
if (hints.length === 0)
|
|
196
|
+
return systemPrompt;
|
|
197
|
+
return `${systemPrompt}\n\n[User Profile] ${hints.join(' ')}`;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
exports.UserProfiler = UserProfiler;
|
|
201
|
+
//# sourceMappingURL=user-profiler.js.map
|
package/dist/schema/oad.d.ts
CHANGED
|
@@ -39,14 +39,14 @@ export declare const VoiceSchema: z.ZodObject<{
|
|
|
39
39
|
language: z.ZodOptional<z.ZodString>;
|
|
40
40
|
}, "strip", z.ZodTypeAny, {
|
|
41
41
|
enabled: boolean;
|
|
42
|
+
language?: string | undefined;
|
|
42
43
|
sttProvider?: string | undefined;
|
|
43
44
|
ttsProvider?: string | undefined;
|
|
44
|
-
language?: string | undefined;
|
|
45
45
|
}, {
|
|
46
|
+
language?: string | undefined;
|
|
46
47
|
enabled?: boolean | undefined;
|
|
47
48
|
sttProvider?: string | undefined;
|
|
48
49
|
ttsProvider?: string | undefined;
|
|
49
|
-
language?: string | undefined;
|
|
50
50
|
}>;
|
|
51
51
|
export declare const WebhookSchema: z.ZodObject<{
|
|
52
52
|
path: z.ZodOptional<z.ZodString>;
|
|
@@ -827,14 +827,14 @@ export declare const SpecSchema: z.ZodObject<{
|
|
|
827
827
|
language: z.ZodOptional<z.ZodString>;
|
|
828
828
|
}, "strip", z.ZodTypeAny, {
|
|
829
829
|
enabled: boolean;
|
|
830
|
+
language?: string | undefined;
|
|
830
831
|
sttProvider?: string | undefined;
|
|
831
832
|
ttsProvider?: string | undefined;
|
|
832
|
-
language?: string | undefined;
|
|
833
833
|
}, {
|
|
834
|
+
language?: string | undefined;
|
|
834
835
|
enabled?: boolean | undefined;
|
|
835
836
|
sttProvider?: string | undefined;
|
|
836
837
|
ttsProvider?: string | undefined;
|
|
837
|
-
language?: string | undefined;
|
|
838
838
|
}>>;
|
|
839
839
|
webhook: z.ZodOptional<z.ZodObject<{
|
|
840
840
|
path: z.ZodOptional<z.ZodString>;
|
|
@@ -1071,9 +1071,9 @@ export declare const SpecSchema: z.ZodObject<{
|
|
|
1071
1071
|
} | undefined;
|
|
1072
1072
|
voice?: {
|
|
1073
1073
|
enabled: boolean;
|
|
1074
|
+
language?: string | undefined;
|
|
1074
1075
|
sttProvider?: string | undefined;
|
|
1075
1076
|
ttsProvider?: string | undefined;
|
|
1076
|
-
language?: string | undefined;
|
|
1077
1077
|
} | undefined;
|
|
1078
1078
|
webhook?: {
|
|
1079
1079
|
path?: string | undefined;
|
|
@@ -1185,10 +1185,10 @@ export declare const SpecSchema: z.ZodObject<{
|
|
|
1185
1185
|
sessionIsolation?: boolean | undefined;
|
|
1186
1186
|
} | undefined;
|
|
1187
1187
|
voice?: {
|
|
1188
|
+
language?: string | undefined;
|
|
1188
1189
|
enabled?: boolean | undefined;
|
|
1189
1190
|
sttProvider?: string | undefined;
|
|
1190
1191
|
ttsProvider?: string | undefined;
|
|
1191
|
-
language?: string | undefined;
|
|
1192
1192
|
} | undefined;
|
|
1193
1193
|
webhook?: {
|
|
1194
1194
|
path?: string | undefined;
|
|
@@ -1573,14 +1573,14 @@ export declare const OADSchema: z.ZodObject<{
|
|
|
1573
1573
|
language: z.ZodOptional<z.ZodString>;
|
|
1574
1574
|
}, "strip", z.ZodTypeAny, {
|
|
1575
1575
|
enabled: boolean;
|
|
1576
|
+
language?: string | undefined;
|
|
1576
1577
|
sttProvider?: string | undefined;
|
|
1577
1578
|
ttsProvider?: string | undefined;
|
|
1578
|
-
language?: string | undefined;
|
|
1579
1579
|
}, {
|
|
1580
|
+
language?: string | undefined;
|
|
1580
1581
|
enabled?: boolean | undefined;
|
|
1581
1582
|
sttProvider?: string | undefined;
|
|
1582
1583
|
ttsProvider?: string | undefined;
|
|
1583
|
-
language?: string | undefined;
|
|
1584
1584
|
}>>;
|
|
1585
1585
|
webhook: z.ZodOptional<z.ZodObject<{
|
|
1586
1586
|
path: z.ZodOptional<z.ZodString>;
|
|
@@ -1817,9 +1817,9 @@ export declare const OADSchema: z.ZodObject<{
|
|
|
1817
1817
|
} | undefined;
|
|
1818
1818
|
voice?: {
|
|
1819
1819
|
enabled: boolean;
|
|
1820
|
+
language?: string | undefined;
|
|
1820
1821
|
sttProvider?: string | undefined;
|
|
1821
1822
|
ttsProvider?: string | undefined;
|
|
1822
|
-
language?: string | undefined;
|
|
1823
1823
|
} | undefined;
|
|
1824
1824
|
webhook?: {
|
|
1825
1825
|
path?: string | undefined;
|
|
@@ -1931,10 +1931,10 @@ export declare const OADSchema: z.ZodObject<{
|
|
|
1931
1931
|
sessionIsolation?: boolean | undefined;
|
|
1932
1932
|
} | undefined;
|
|
1933
1933
|
voice?: {
|
|
1934
|
+
language?: string | undefined;
|
|
1934
1935
|
enabled?: boolean | undefined;
|
|
1935
1936
|
sttProvider?: string | undefined;
|
|
1936
1937
|
ttsProvider?: string | undefined;
|
|
1937
|
-
language?: string | undefined;
|
|
1938
1938
|
} | undefined;
|
|
1939
1939
|
webhook?: {
|
|
1940
1940
|
path?: string | undefined;
|
|
@@ -2092,9 +2092,9 @@ export declare const OADSchema: z.ZodObject<{
|
|
|
2092
2092
|
} | undefined;
|
|
2093
2093
|
voice?: {
|
|
2094
2094
|
enabled: boolean;
|
|
2095
|
+
language?: string | undefined;
|
|
2095
2096
|
sttProvider?: string | undefined;
|
|
2096
2097
|
ttsProvider?: string | undefined;
|
|
2097
|
-
language?: string | undefined;
|
|
2098
2098
|
} | undefined;
|
|
2099
2099
|
webhook?: {
|
|
2100
2100
|
path?: string | undefined;
|
|
@@ -2223,10 +2223,10 @@ export declare const OADSchema: z.ZodObject<{
|
|
|
2223
2223
|
sessionIsolation?: boolean | undefined;
|
|
2224
2224
|
} | undefined;
|
|
2225
2225
|
voice?: {
|
|
2226
|
+
language?: string | undefined;
|
|
2226
2227
|
enabled?: boolean | undefined;
|
|
2227
2228
|
sttProvider?: string | undefined;
|
|
2228
2229
|
ttsProvider?: string | undefined;
|
|
2229
|
-
language?: string | undefined;
|
|
2230
2230
|
} | undefined;
|
|
2231
2231
|
webhook?: {
|
|
2232
2232
|
path?: string | undefined;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approvals Module - v1.0.0
|
|
3
|
+
* Policy-based exec approval system with queue, expiry, history, and callbacks.
|
|
4
|
+
*/
|
|
5
|
+
export type ExecApprovalPolicy = 'always' | 'elevated-only' | 'never' | 'allowlist';
|
|
6
|
+
export interface ExecApprovalRequest {
|
|
7
|
+
id: string;
|
|
8
|
+
command: string;
|
|
9
|
+
elevated: boolean;
|
|
10
|
+
requestedAt: number;
|
|
11
|
+
expiresAt: number;
|
|
12
|
+
status: 'pending' | 'approved' | 'denied' | 'expired';
|
|
13
|
+
approvedBy?: string;
|
|
14
|
+
reason?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ExecApprovalHistory {
|
|
17
|
+
request: ExecApprovalRequest;
|
|
18
|
+
resolvedAt: number;
|
|
19
|
+
}
|
|
20
|
+
export type ApprovalRequestCallback = (request: ExecApprovalRequest) => void;
|
|
21
|
+
export declare class ExecApprovalManager {
|
|
22
|
+
private policy;
|
|
23
|
+
private pending;
|
|
24
|
+
private history;
|
|
25
|
+
private allowedCommands;
|
|
26
|
+
private expiryMs;
|
|
27
|
+
private onRequestCallback?;
|
|
28
|
+
private expiryTimer?;
|
|
29
|
+
constructor(options?: {
|
|
30
|
+
policy?: ExecApprovalPolicy;
|
|
31
|
+
expiryMs?: number;
|
|
32
|
+
allowedCommands?: string[];
|
|
33
|
+
onRequest?: ApprovalRequestCallback;
|
|
34
|
+
});
|
|
35
|
+
getPolicy(): ExecApprovalPolicy;
|
|
36
|
+
setPolicy(p: ExecApprovalPolicy): void;
|
|
37
|
+
addAllowedCommand(cmd: string): void;
|
|
38
|
+
removeAllowedCommand(cmd: string): void;
|
|
39
|
+
getAllowedCommands(): string[];
|
|
40
|
+
needsApproval(command: string, elevated: boolean): boolean;
|
|
41
|
+
private isAllowed;
|
|
42
|
+
request(command: string, elevated?: boolean): ExecApprovalRequest;
|
|
43
|
+
approve(id: string, approver: string): ExecApprovalRequest;
|
|
44
|
+
deny(id: string, approver: string, reason?: string): ExecApprovalRequest;
|
|
45
|
+
getPending(): ExecApprovalRequest[];
|
|
46
|
+
getHistory(): ExecApprovalHistory[];
|
|
47
|
+
getRequest(id: string): ExecApprovalRequest | undefined;
|
|
48
|
+
private expirePending;
|
|
49
|
+
/** Force expire check (for testing) */
|
|
50
|
+
checkExpiry(): void;
|
|
51
|
+
destroy(): void;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=approvals.d.ts.map
|