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.
Files changed (151) hide show
  1. package/README.md +30 -24
  2. package/dist/channels/dingtalk.d.ts +17 -0
  3. package/dist/channels/dingtalk.js +38 -0
  4. package/dist/channels/googlechat.d.ts +14 -0
  5. package/dist/channels/googlechat.js +37 -0
  6. package/dist/channels/imessage.d.ts +13 -0
  7. package/dist/channels/imessage.js +28 -0
  8. package/dist/channels/irc.d.ts +20 -0
  9. package/dist/channels/irc.js +71 -0
  10. package/dist/channels/line.d.ts +14 -0
  11. package/dist/channels/line.js +28 -0
  12. package/dist/channels/matrix.d.ts +15 -0
  13. package/dist/channels/matrix.js +28 -0
  14. package/dist/channels/mattermost.d.ts +18 -0
  15. package/dist/channels/mattermost.js +49 -0
  16. package/dist/channels/msteams.d.ts +14 -0
  17. package/dist/channels/msteams.js +28 -0
  18. package/dist/channels/nostr.d.ts +14 -0
  19. package/dist/channels/nostr.js +28 -0
  20. package/dist/channels/qq.d.ts +15 -0
  21. package/dist/channels/qq.js +28 -0
  22. package/dist/channels/signal.d.ts +14 -0
  23. package/dist/channels/signal.js +28 -0
  24. package/dist/channels/sms.d.ts +15 -0
  25. package/dist/channels/sms.js +28 -0
  26. package/dist/channels/twitch.d.ts +17 -0
  27. package/dist/channels/twitch.js +59 -0
  28. package/dist/channels/voice-call.d.ts +27 -0
  29. package/dist/channels/voice-call.js +82 -0
  30. package/dist/channels/whatsapp.d.ts +14 -0
  31. package/dist/channels/whatsapp.js +28 -0
  32. package/dist/cli.js +36 -0
  33. package/dist/core/api-server.d.ts +25 -0
  34. package/dist/core/api-server.js +286 -0
  35. package/dist/core/audio.d.ts +50 -0
  36. package/dist/core/audio.js +68 -0
  37. package/dist/core/context-discovery.d.ts +16 -0
  38. package/dist/core/context-discovery.js +107 -0
  39. package/dist/core/context-refs.d.ts +29 -0
  40. package/dist/core/context-refs.js +162 -0
  41. package/dist/core/gateway.d.ts +53 -0
  42. package/dist/core/gateway.js +80 -0
  43. package/dist/core/heartbeat.d.ts +19 -0
  44. package/dist/core/heartbeat.js +50 -0
  45. package/dist/core/hooks.d.ts +28 -0
  46. package/dist/core/hooks.js +82 -0
  47. package/dist/core/ide-bridge.d.ts +53 -0
  48. package/dist/core/ide-bridge.js +97 -0
  49. package/dist/core/node-network.d.ts +23 -0
  50. package/dist/core/node-network.js +77 -0
  51. package/dist/core/profiles.d.ts +27 -0
  52. package/dist/core/profiles.js +131 -0
  53. package/dist/core/sandbox.d.ts +25 -0
  54. package/dist/core/sandbox.js +84 -1
  55. package/dist/core/session-manager.d.ts +33 -0
  56. package/dist/core/session-manager.js +157 -0
  57. package/dist/core/vision.d.ts +45 -0
  58. package/dist/core/vision.js +177 -0
  59. package/dist/index.d.ts +64 -1
  60. package/dist/index.js +86 -3
  61. package/dist/memory/context-compressor.d.ts +43 -0
  62. package/dist/memory/context-compressor.js +167 -0
  63. package/dist/memory/index.d.ts +4 -0
  64. package/dist/memory/index.js +5 -1
  65. package/dist/memory/user-profiler.d.ts +50 -0
  66. package/dist/memory/user-profiler.js +201 -0
  67. package/dist/schema/oad.d.ts +12 -12
  68. package/dist/security/approvals.d.ts +53 -0
  69. package/dist/security/approvals.js +115 -0
  70. package/dist/security/elevated.d.ts +41 -0
  71. package/dist/security/elevated.js +89 -0
  72. package/dist/security/index.d.ts +6 -0
  73. package/dist/security/index.js +7 -1
  74. package/dist/security/secrets.d.ts +34 -0
  75. package/dist/security/secrets.js +115 -0
  76. package/dist/tools/builtin/browser.d.ts +47 -0
  77. package/dist/tools/builtin/browser.js +284 -0
  78. package/dist/tools/builtin/home-assistant.d.ts +12 -0
  79. package/dist/tools/builtin/home-assistant.js +126 -0
  80. package/dist/tools/builtin/index.d.ts +6 -1
  81. package/dist/tools/builtin/index.js +18 -2
  82. package/dist/tools/builtin/rl-tools.d.ts +13 -0
  83. package/dist/tools/builtin/rl-tools.js +228 -0
  84. package/dist/tools/builtin/vision.d.ts +6 -0
  85. package/dist/tools/builtin/vision.js +61 -0
  86. package/package.json +2 -2
  87. package/src/channels/dingtalk.ts +46 -0
  88. package/src/channels/googlechat.ts +42 -0
  89. package/src/channels/imessage.ts +32 -0
  90. package/src/channels/irc.ts +82 -0
  91. package/src/channels/line.ts +33 -0
  92. package/src/channels/matrix.ts +34 -0
  93. package/src/channels/mattermost.ts +57 -0
  94. package/src/channels/msteams.ts +33 -0
  95. package/src/channels/nostr.ts +33 -0
  96. package/src/channels/qq.ts +34 -0
  97. package/src/channels/signal.ts +33 -0
  98. package/src/channels/sms.ts +34 -0
  99. package/src/channels/twitch.ts +65 -0
  100. package/src/channels/voice-call.ts +100 -0
  101. package/src/channels/whatsapp.ts +33 -0
  102. package/src/cli.ts +40 -0
  103. package/src/core/api-server.ts +277 -0
  104. package/src/core/audio.ts +98 -0
  105. package/src/core/context-discovery.ts +85 -0
  106. package/src/core/context-refs.ts +140 -0
  107. package/src/core/gateway.ts +106 -0
  108. package/src/core/heartbeat.ts +51 -0
  109. package/src/core/hooks.ts +105 -0
  110. package/src/core/ide-bridge.ts +133 -0
  111. package/src/core/node-network.ts +86 -0
  112. package/src/core/profiles.ts +122 -0
  113. package/src/core/sandbox.ts +100 -0
  114. package/src/core/session-manager.ts +137 -0
  115. package/src/core/vision.ts +180 -0
  116. package/src/index.ts +84 -1
  117. package/src/memory/context-compressor.ts +189 -0
  118. package/src/memory/index.ts +4 -0
  119. package/src/memory/user-profiler.ts +215 -0
  120. package/src/security/approvals.ts +143 -0
  121. package/src/security/elevated.ts +105 -0
  122. package/src/security/index.ts +6 -0
  123. package/src/security/secrets.ts +129 -0
  124. package/src/tools/builtin/browser.ts +299 -0
  125. package/src/tools/builtin/home-assistant.ts +116 -0
  126. package/src/tools/builtin/index.ts +9 -2
  127. package/src/tools/builtin/rl-tools.ts +243 -0
  128. package/src/tools/builtin/vision.ts +64 -0
  129. package/tests/api-server.test.ts +148 -0
  130. package/tests/approvals.test.ts +89 -0
  131. package/tests/audio.test.ts +40 -0
  132. package/tests/browser.test.ts +179 -0
  133. package/tests/builtin-tools.test.ts +83 -83
  134. package/tests/channels-extra.test.ts +45 -0
  135. package/tests/context-compressor.test.ts +172 -0
  136. package/tests/context-refs.test.ts +121 -0
  137. package/tests/elevated.test.ts +69 -0
  138. package/tests/gateway.test.ts +63 -71
  139. package/tests/home-assistant.test.ts +40 -0
  140. package/tests/hooks.test.ts +79 -0
  141. package/tests/ide-bridge.test.ts +38 -0
  142. package/tests/node-network.test.ts +74 -0
  143. package/tests/profiles.test.ts +61 -0
  144. package/tests/rl-tools.test.ts +93 -0
  145. package/tests/sandbox-manager.test.ts +46 -0
  146. package/tests/secrets.test.ts +107 -0
  147. package/tests/tools/builtin-extended.test.ts +138 -138
  148. package/tests/user-profiler.test.ts +169 -0
  149. package/tests/v090-features.test.ts +254 -0
  150. package/tests/vision.test.ts +61 -0
  151. 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
@@ -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;
@@ -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
@@ -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