yakmesh 2.8.2 → 3.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/CHANGELOG.md +637 -0
- package/CONTRIBUTING.md +42 -0
- package/Caddyfile +77 -0
- package/README.md +119 -29
- package/adapters/adapter-mlv-bible/README.md +124 -0
- package/adapters/adapter-mlv-bible/index.js +400 -0
- package/adapters/chat-mod-adapter.js +532 -0
- package/adapters/content-adapter.js +273 -0
- package/content/api.js +50 -41
- package/content/index.js +2 -2
- package/content/store.js +355 -173
- package/dashboard/index.html +19 -3
- package/database/replication.js +117 -37
- package/docs/CRYPTO-AGILITY.md +204 -0
- package/docs/MTLS-RESEARCH.md +367 -0
- package/docs/NAMCHE-SPEC.md +681 -0
- package/docs/PEERQUANTA-YAKMESH-INTEGRATION.md +407 -0
- package/docs/PRECISION-DISCLOSURE.md +96 -0
- package/docs/README.md +76 -0
- package/docs/ROADMAP-2.4.0.md +447 -0
- package/docs/ROADMAP-2.5.0.md +244 -0
- package/docs/SECURITY-AUDIT-REPORT.md +306 -0
- package/docs/SST-INTEGRATION.md +712 -0
- package/docs/STEADYWATCH-IMPLEMENTATION.md +303 -0
- package/docs/TERNARY-AUDIT-REPORT.md +247 -0
- package/docs/TME-FAQ.md +221 -0
- package/docs/WHITEPAPER.md +623 -0
- package/docs/adapters.html +1001 -0
- package/docs/advanced-systems.html +1045 -0
- package/docs/annex.html +1046 -0
- package/docs/api.html +970 -0
- package/docs/business/response-templates.md +160 -0
- package/docs/c2c.html +1225 -0
- package/docs/cli.html +1332 -0
- package/docs/configuration.html +1248 -0
- package/docs/darshan.html +1085 -0
- package/docs/dharma.html +966 -0
- package/docs/docs-bundle.html +1075 -0
- package/docs/docs.css +3120 -0
- package/docs/docs.js +556 -0
- package/docs/doko.html +969 -0
- package/docs/geo-proof.html +858 -0
- package/docs/getting-started.html +840 -0
- package/docs/gumba-tutorial.html +1144 -0
- package/docs/gumba.html +1098 -0
- package/docs/index.html +914 -0
- package/docs/jhilke.html +1312 -0
- package/docs/karma.html +1100 -0
- package/docs/katha.html +1037 -0
- package/docs/lama.html +978 -0
- package/docs/mandala.html +1067 -0
- package/docs/mani.html +964 -0
- package/docs/mantra.html +967 -0
- package/docs/mesh.html +1409 -0
- package/docs/nakpak.html +869 -0
- package/docs/namche.html +928 -0
- package/docs/nav-order.json +53 -0
- package/docs/prahari.html +1043 -0
- package/docs/prism-bash.min.js +1 -0
- package/docs/prism-javascript.min.js +1 -0
- package/docs/prism-json.min.js +1 -0
- package/docs/prism-tomorrow.min.css +1 -0
- package/docs/prism.min.js +1 -0
- package/docs/privacy.html +699 -0
- package/docs/quick-reference.html +1181 -0
- package/docs/sakshi.html +1402 -0
- package/docs/sandboxing.md +386 -0
- package/docs/seva.html +911 -0
- package/docs/sherpa.html +871 -0
- package/docs/studio.html +860 -0
- package/docs/stupa.html +995 -0
- package/docs/tailwind.min.css +2 -0
- package/docs/tattva.html +1332 -0
- package/docs/terms.html +686 -0
- package/docs/time-server-deployment.md +166 -0
- package/docs/time-sources.html +1392 -0
- package/docs/tivra.html +1127 -0
- package/docs/trademark-policy.html +686 -0
- package/docs/tribhuj.html +1183 -0
- package/docs/trust-security.html +1029 -0
- package/docs/tutorials/backup-recovery.html +654 -0
- package/docs/tutorials/dashboard.html +604 -0
- package/docs/tutorials/domain-setup.html +605 -0
- package/docs/tutorials/host-website.html +456 -0
- package/docs/tutorials/mesh-network.html +505 -0
- package/docs/tutorials/mobile-access.html +445 -0
- package/docs/tutorials/privacy.html +467 -0
- package/docs/tutorials/raspberry-pi.html +600 -0
- package/docs/tutorials/security-basics.html +539 -0
- package/docs/tutorials/share-files.html +431 -0
- package/docs/tutorials/troubleshooting.html +637 -0
- package/docs/tutorials/trust-karma.html +419 -0
- package/docs/tutorials/yak-protocol.html +456 -0
- package/docs/tutorials.html +1034 -0
- package/docs/vani.html +1270 -0
- package/docs/webserver.html +809 -0
- package/docs/yak-protocol.html +940 -0
- package/docs/yak-timeserver-design.md +475 -0
- package/docs/yakapp.html +1015 -0
- package/docs/ypc27.html +1069 -0
- package/docs/yurt.html +1344 -0
- package/embedded-docs/bundle.js +334 -74
- package/gossip/protocol.js +247 -27
- package/identity/key-resolver.js +262 -0
- package/identity/machine-seed.js +632 -0
- package/identity/node-key.js +669 -368
- package/identity/tribhuj-ratchet.js +506 -0
- package/knowledge-base.js +37 -8
- package/launcher/yakmesh.bat +62 -0
- package/launcher/yakmesh.sh +70 -0
- package/mesh/annex.js +462 -108
- package/mesh/beacon-broadcast.js +113 -1
- package/mesh/darshan.js +1718 -0
- package/mesh/gumba.js +1567 -0
- package/mesh/jhilke.js +651 -0
- package/mesh/katha.js +1012 -0
- package/mesh/nakpak-routing.js +8 -5
- package/mesh/network.js +724 -34
- package/mesh/pulse-sync.js +4 -1
- package/mesh/rate-limiter.js +127 -15
- package/mesh/seva.js +526 -0
- package/mesh/sherpa-discovery.js +89 -8
- package/mesh/sybil-defense.js +19 -5
- package/mesh/temporal-encoder.js +4 -3
- package/mesh/vani.js +1364 -0
- package/mesh/yurt.js +1340 -0
- package/models/entropy-sentinel.onnx +0 -0
- package/models/karma-trust.onnx +0 -0
- package/models/manifest.json +43 -0
- package/models/sakshi-anomaly.onnx +0 -0
- package/oracle/code-proof-protocol.js +7 -6
- package/oracle/codebase-lock.js +257 -28
- package/oracle/index.js +74 -15
- package/oracle/ma902-snmp.js +678 -0
- package/oracle/module-sealer.js +5 -3
- package/oracle/network-identity.js +16 -0
- package/oracle/packet-checksum.js +201 -0
- package/oracle/sst.js +579 -0
- package/oracle/ternary-144t.js +714 -0
- package/oracle/ternary-ml.js +481 -0
- package/oracle/time-api.js +239 -0
- package/oracle/time-source.js +137 -47
- package/oracle/validation-oracle-hardened.js +1111 -1071
- package/oracle/validation-oracle.js +4 -2
- package/oracle/ypc27.js +211 -0
- package/package.json +20 -3
- package/protocol/yak-handler.js +35 -9
- package/protocol/yak-protocol.js +28 -13
- package/reference/cpp/yakmesh_mceliece_shard.cpp +168 -0
- package/reference/cpp/yakmesh_ypc27.cpp +179 -0
- package/sbom.json +87 -0
- package/scripts/security-audit.mjs +264 -0
- package/scripts/update-docs-nav.js +194 -0
- package/scripts/update-docs-sidebar.cjs +164 -0
- package/security/crypto-config.js +4 -3
- package/security/dharma-moderation.js +517 -0
- package/security/doko-identity.js +193 -143
- package/security/domain-consensus.js +86 -85
- package/security/fs-hardening.js +620 -0
- package/security/hardware-attestation.js +5 -3
- package/security/hybrid-trust.js +227 -87
- package/security/karma-rate-limiter.js +692 -0
- package/security/khata-protocol.js +22 -21
- package/security/khata-trust-integration.js +277 -150
- package/security/memory-safety.js +635 -0
- package/security/mesh-auth.js +11 -10
- package/security/mesh-revocation.js +373 -5
- package/security/namche-gateway.js +298 -69
- package/security/sakshi.js +460 -3
- package/security/sangha.js +770 -0
- package/security/secure-config.js +473 -0
- package/security/silicon-parity.js +13 -10
- package/security/steadywatch.js +1142 -0
- package/security/strike-system.js +32 -3
- package/security/temporal-signing.js +488 -0
- package/security/trit-commitment.js +464 -0
- package/server/crypto/annex.js +247 -0
- package/server/darshan-api.js +343 -0
- package/server/index.js +3259 -362
- package/server/komm-api.js +668 -0
- package/utils/accel.js +2273 -0
- package/utils/ternary-id.js +79 -0
- package/utils/verify-worker.js +57 -0
- package/webserver/index.js +95 -5
- package/assets/yakmesh-logo.png +0 -0
- package/assets/yakmesh-logo.svg +0 -80
- package/assets/yakmesh-logo2.png +0 -0
- package/assets/yakmesh-logo2sm.png +0 -0
- package/assets/ymsm.png +0 -0
- package/website/assets/silhouettes/adapters.svg +0 -107
- package/website/assets/silhouettes/api-endpoints.svg +0 -115
- package/website/assets/silhouettes/atomic-clock.svg +0 -83
- package/website/assets/silhouettes/base-camp.svg +0 -81
- package/website/assets/silhouettes/bridge.svg +0 -69
- package/website/assets/silhouettes/docs-bundle.svg +0 -113
- package/website/assets/silhouettes/doko-basket.svg +0 -70
- package/website/assets/silhouettes/fortress.svg +0 -93
- package/website/assets/silhouettes/gateway.svg +0 -54
- package/website/assets/silhouettes/gears.svg +0 -93
- package/website/assets/silhouettes/globe-satellite.svg +0 -67
- package/website/assets/silhouettes/karma-wheel.svg +0 -137
- package/website/assets/silhouettes/lama-council.svg +0 -141
- package/website/assets/silhouettes/mandala-network.svg +0 -169
- package/website/assets/silhouettes/mani-stones.svg +0 -149
- package/website/assets/silhouettes/mantra-wheel.svg +0 -116
- package/website/assets/silhouettes/mesh-nodes.svg +0 -113
- package/website/assets/silhouettes/nakpak.svg +0 -56
- package/website/assets/silhouettes/peak-lightning.svg +0 -73
- package/website/assets/silhouettes/sherpa.svg +0 -69
- package/website/assets/silhouettes/stupa-tower.svg +0 -119
- package/website/assets/silhouettes/tattva-eye.svg +0 -78
- package/website/assets/silhouettes/terminal.svg +0 -74
- package/website/assets/silhouettes/webserver.svg +0 -145
- package/website/assets/silhouettes/yak.svg +0 -78
- package/website/assets/yakmesh-logo.png +0 -0
- package/website/assets/yakmesh-logo.webp +0 -0
- package/website/assets/yakmesh-logo128x140.webp +0 -0
- package/website/assets/yakmesh-logo2.png +0 -0
- package/website/assets/yakmesh-logo2.svg +0 -51
- package/website/assets/yakmesh-logo40x44.webp +0 -0
- package/website/assets/yakmesh.gif +0 -0
- package/website/assets/yakmesh.ico +0 -0
- package/website/assets/yakmesh.jpg +0 -0
- package/website/assets/yakmesh.pdf +0 -0
- package/website/assets/yakmesh.png +0 -0
- package/website/assets/yakmesh.svg +0 -70
- package/website/assets/yakmesh128.webp +0 -0
- package/website/assets/yakmesh32.png +0 -0
- package/website/assets/yakmesh32.svg +0 -65
- package/website/assets/yakmesh32o.ico +0 -2
- package/website/assets/yakmesh32o.svg +0 -65
- package/website/assets/yakmesh32o.svgz +0 -0
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Yakmesh - Chat Mod Adapter Framework
|
|
3
|
+
*
|
|
4
|
+
* Secure framework for chat modification plugins that integrate with KATHA.
|
|
5
|
+
* Designed to prevent manipulation and abuse by malicious adapters.
|
|
6
|
+
*
|
|
7
|
+
* KEY SECURITY PRINCIPLES:
|
|
8
|
+
* 1. Capability Declaration - Adapters MUST declare what they can do
|
|
9
|
+
* 2. Sandboxed Execution - Adapters cannot access raw message content without permission
|
|
10
|
+
* 3. Content Signing - All adapter-generated content is signed and verifiable
|
|
11
|
+
* 4. Rate Limiting - Prevents spam/flooding
|
|
12
|
+
* 5. GUMBA Integration - Respects room role permissions
|
|
13
|
+
* 6. DHARMA Moderation - Behavior-based content filtering (v3.0)
|
|
14
|
+
*
|
|
15
|
+
* @module adapters/chat-mod-adapter
|
|
16
|
+
* @version 1.1.0
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { EventEmitter } from 'events';
|
|
20
|
+
import { createHash } from 'crypto';
|
|
21
|
+
import { DharmaModerator, MODERATION_ACTIONS } from '../security/dharma-moderation.js';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Chat modification capabilities
|
|
25
|
+
* Adapters MUST declare their capabilities upfront
|
|
26
|
+
*/
|
|
27
|
+
export const CHAT_MOD_CAPABILITIES = {
|
|
28
|
+
// Message handling
|
|
29
|
+
MSG_READ: 'msg:read', // Can read message content (requires consent)
|
|
30
|
+
MSG_RESPOND: 'msg:respond', // Can send responses to messages
|
|
31
|
+
MSG_EMBED: 'msg:embed', // Can embed rich content in messages
|
|
32
|
+
MSG_REACT: 'msg:react', // Can add reactions
|
|
33
|
+
|
|
34
|
+
// Command handling
|
|
35
|
+
CMD_SLASH: 'cmd:slash', // Can register slash commands (e.g., /bible)
|
|
36
|
+
CMD_MENTION: 'cmd:mention', // Can respond to @mentions
|
|
37
|
+
CMD_PREFIX: 'cmd:prefix', // Can handle custom prefixes
|
|
38
|
+
|
|
39
|
+
// Content generation
|
|
40
|
+
GEN_QUOTE: 'gen:quote', // Can generate quotes
|
|
41
|
+
GEN_CARD: 'gen:card', // Can generate rich cards
|
|
42
|
+
GEN_LINK: 'gen:link', // Can generate links
|
|
43
|
+
|
|
44
|
+
// Special permissions (require explicit user opt-in)
|
|
45
|
+
SPECIAL_DM: 'special:dm', // Can send direct messages
|
|
46
|
+
SPECIAL_THREAD: 'special:thread',// Can create threads
|
|
47
|
+
SPECIAL_PIN: 'special:pin', // Can pin messages (requires ADMIN)
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Security levels for chat mods
|
|
52
|
+
*/
|
|
53
|
+
export const SECURITY_LEVELS = {
|
|
54
|
+
SANDBOX: 'sandbox', // Maximum restrictions, no raw content access
|
|
55
|
+
STANDARD: 'standard', // Normal restrictions, declared capabilities only
|
|
56
|
+
TRUSTED: 'trusted', // Reduced restrictions (user explicitly trusts)
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Adapter manifest - security declaration
|
|
61
|
+
*/
|
|
62
|
+
export class ChatModManifest {
|
|
63
|
+
constructor({
|
|
64
|
+
id,
|
|
65
|
+
name,
|
|
66
|
+
version = '1.0.0',
|
|
67
|
+
author = null,
|
|
68
|
+
description = '',
|
|
69
|
+
capabilities = [],
|
|
70
|
+
commands = [], // Slash commands this adapter handles
|
|
71
|
+
triggers = [], // Message patterns that trigger this adapter
|
|
72
|
+
rateLimit = { messages: 10, window: 60000 }, // 10 messages per minute
|
|
73
|
+
securityLevel = SECURITY_LEVELS.SANDBOX,
|
|
74
|
+
} = {}) {
|
|
75
|
+
this.id = id;
|
|
76
|
+
this.name = name;
|
|
77
|
+
this.version = version;
|
|
78
|
+
this.author = author;
|
|
79
|
+
this.description = description;
|
|
80
|
+
this.capabilities = new Set(capabilities);
|
|
81
|
+
this.commands = commands;
|
|
82
|
+
this.triggers = triggers;
|
|
83
|
+
this.rateLimit = rateLimit;
|
|
84
|
+
this.securityLevel = securityLevel;
|
|
85
|
+
|
|
86
|
+
// Generate manifest hash for verification
|
|
87
|
+
this.hash = this._computeHash();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
_computeHash() {
|
|
91
|
+
const data = JSON.stringify({
|
|
92
|
+
id: this.id,
|
|
93
|
+
name: this.name,
|
|
94
|
+
version: this.version,
|
|
95
|
+
capabilities: Array.from(this.capabilities).sort(),
|
|
96
|
+
commands: this.commands,
|
|
97
|
+
});
|
|
98
|
+
return createHash('sha256').update(data).digest('hex').slice(0, 16);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
toJSON() {
|
|
102
|
+
return {
|
|
103
|
+
...this,
|
|
104
|
+
capabilities: Array.from(this.capabilities),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Abstract base class for chat modification adapters
|
|
111
|
+
*/
|
|
112
|
+
export class ChatModAdapter extends EventEmitter {
|
|
113
|
+
/**
|
|
114
|
+
* @param {ChatModManifest} manifest - Security manifest
|
|
115
|
+
* @param {Object} config - Adapter configuration
|
|
116
|
+
*/
|
|
117
|
+
constructor(manifest, config = {}) {
|
|
118
|
+
super();
|
|
119
|
+
|
|
120
|
+
if (new.target === ChatModAdapter) {
|
|
121
|
+
throw new Error('ChatModAdapter is abstract and cannot be instantiated directly');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!(manifest instanceof ChatModManifest)) {
|
|
125
|
+
throw new Error('ChatModAdapter requires a ChatModManifest');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.manifest = manifest;
|
|
129
|
+
this.config = config;
|
|
130
|
+
this.katha = config.katha || null;
|
|
131
|
+
this.gumba = config.gumba || null;
|
|
132
|
+
|
|
133
|
+
// DHARMA content moderation (v3.0)
|
|
134
|
+
// Behavior-based filtering - no identity discrimination
|
|
135
|
+
this.moderator = config.moderator || new DharmaModerator();
|
|
136
|
+
this.enableModeration = config.enableModeration !== false; // Default: enabled
|
|
137
|
+
|
|
138
|
+
// Rate limiting state
|
|
139
|
+
this._rateLimitWindow = [];
|
|
140
|
+
this._rateLimitMax = manifest.rateLimit.messages;
|
|
141
|
+
this._rateLimitWindowMs = manifest.rateLimit.window;
|
|
142
|
+
|
|
143
|
+
// Statistics
|
|
144
|
+
this.stats = {
|
|
145
|
+
messagesProcessed: 0,
|
|
146
|
+
commandsHandled: 0,
|
|
147
|
+
responsesGenerated: 0,
|
|
148
|
+
rateLimitHits: 0,
|
|
149
|
+
moderationBlocks: 0,
|
|
150
|
+
errors: [],
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Validate manifest
|
|
154
|
+
this._validateManifest();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Initialize the adapter
|
|
159
|
+
* @abstract
|
|
160
|
+
*/
|
|
161
|
+
async init() {
|
|
162
|
+
throw new Error('init() must be implemented by subclass');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Handle an incoming message
|
|
167
|
+
* Called by KATHA when messages match triggers
|
|
168
|
+
* @param {Object} context - Message context (sanitized based on capabilities)
|
|
169
|
+
* @returns {Promise<Object|null>} Response to send, or null
|
|
170
|
+
*/
|
|
171
|
+
async handleMessage(context) {
|
|
172
|
+
// Rate limit check
|
|
173
|
+
if (!this._checkRateLimit()) {
|
|
174
|
+
this.stats.rateLimitHits++;
|
|
175
|
+
this.emit('rate-limited', { adapterId: this.manifest.id });
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Security: Strip sensitive data based on capabilities
|
|
180
|
+
const sanitizedContext = this._sanitizeContext(context);
|
|
181
|
+
|
|
182
|
+
// Call subclass implementation
|
|
183
|
+
const response = await this.onMessage(sanitizedContext);
|
|
184
|
+
|
|
185
|
+
if (response) {
|
|
186
|
+
this.stats.responsesGenerated++;
|
|
187
|
+
// DHARMA moderation check on output
|
|
188
|
+
const moderated = await this._moderateResponse(response, context);
|
|
189
|
+
if (moderated && moderated.blocked) {
|
|
190
|
+
return moderated; // Return moderation notice
|
|
191
|
+
}
|
|
192
|
+
return this._signResponse(moderated || response);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Handle a slash command
|
|
200
|
+
* @param {string} command - Command name (without /)
|
|
201
|
+
* @param {string[]} args - Command arguments
|
|
202
|
+
* @param {Object} context - Command context
|
|
203
|
+
* @returns {Promise<Object|null>}
|
|
204
|
+
*/
|
|
205
|
+
async handleCommand(command, args, context) {
|
|
206
|
+
if (!this.manifest.capabilities.has(CHAT_MOD_CAPABILITIES.CMD_SLASH)) {
|
|
207
|
+
throw new Error('Adapter does not have slash command capability');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (!this.manifest.commands.includes(command)) {
|
|
211
|
+
return null; // Not our command
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!this._checkRateLimit()) {
|
|
215
|
+
this.stats.rateLimitHits++;
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
this.stats.commandsHandled++;
|
|
220
|
+
|
|
221
|
+
const response = await this.onCommand(command, args, this._sanitizeContext(context));
|
|
222
|
+
|
|
223
|
+
if (response) {
|
|
224
|
+
// DHARMA moderation check on output
|
|
225
|
+
const moderated = await this._moderateResponse(response, context);
|
|
226
|
+
if (moderated && moderated.blocked) {
|
|
227
|
+
return moderated; // Return moderation notice
|
|
228
|
+
}
|
|
229
|
+
return this._signResponse(moderated || response);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Handle incoming message (implement in subclass)
|
|
237
|
+
* @abstract
|
|
238
|
+
* @param {Object} context - Sanitized message context
|
|
239
|
+
* @returns {Promise<Object|null>}
|
|
240
|
+
*/
|
|
241
|
+
async onMessage(context) {
|
|
242
|
+
// Default: no response
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Handle slash command (implement in subclass)
|
|
248
|
+
* @abstract
|
|
249
|
+
* @param {string} command - Command name
|
|
250
|
+
* @param {string[]} args - Arguments
|
|
251
|
+
* @param {Object} context - Context
|
|
252
|
+
* @returns {Promise<Object|null>}
|
|
253
|
+
*/
|
|
254
|
+
async onCommand(command, args, context) {
|
|
255
|
+
throw new Error('onCommand() must be implemented for slash command adapters');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Rate limit check
|
|
260
|
+
* @private
|
|
261
|
+
*/
|
|
262
|
+
_checkRateLimit() {
|
|
263
|
+
const now = Date.now();
|
|
264
|
+
|
|
265
|
+
// Remove expired entries
|
|
266
|
+
this._rateLimitWindow = this._rateLimitWindow.filter(
|
|
267
|
+
t => now - t < this._rateLimitWindowMs
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
if (this._rateLimitWindow.length >= this._rateLimitMax) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
this._rateLimitWindow.push(now);
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Sanitize context based on declared capabilities
|
|
280
|
+
* @private
|
|
281
|
+
*/
|
|
282
|
+
_sanitizeContext(context) {
|
|
283
|
+
const sanitized = {
|
|
284
|
+
roomId: context.roomId,
|
|
285
|
+
senderId: context.senderId,
|
|
286
|
+
senderRole: context.senderRole,
|
|
287
|
+
timestamp: context.timestamp,
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// Only include message content if adapter has read permission
|
|
291
|
+
if (this.manifest.capabilities.has(CHAT_MOD_CAPABILITIES.MSG_READ)) {
|
|
292
|
+
sanitized.content = context.content;
|
|
293
|
+
sanitized.messageId = context.messageId;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Only include thread info if adapter has thread permission
|
|
297
|
+
if (this.manifest.capabilities.has(CHAT_MOD_CAPABILITIES.SPECIAL_THREAD)) {
|
|
298
|
+
sanitized.threadId = context.threadId;
|
|
299
|
+
sanitized.parentId = context.parentId;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return sanitized;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Sign a response for verification
|
|
307
|
+
* @private
|
|
308
|
+
*/
|
|
309
|
+
_signResponse(response) {
|
|
310
|
+
return {
|
|
311
|
+
...response,
|
|
312
|
+
_adapter: {
|
|
313
|
+
id: this.manifest.id,
|
|
314
|
+
name: this.manifest.name,
|
|
315
|
+
version: this.manifest.version,
|
|
316
|
+
manifestHash: this.manifest.hash,
|
|
317
|
+
timestamp: Date.now(),
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Check content against DHARMA moderation rules
|
|
324
|
+
* @private
|
|
325
|
+
* @param {string} content - Content to check
|
|
326
|
+
* @param {Object} context - Context for logging
|
|
327
|
+
* @returns {Promise<Object>} Moderation result
|
|
328
|
+
*/
|
|
329
|
+
async _moderateContent(content, context = {}) {
|
|
330
|
+
if (!this.enableModeration || !content) {
|
|
331
|
+
return { allowed: true };
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const result = await this.moderator.checkContent(content, {
|
|
335
|
+
adapterId: this.manifest.id,
|
|
336
|
+
...context,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
if (!result.allowed) {
|
|
340
|
+
this.stats.moderationBlocks++;
|
|
341
|
+
this.emit('content-blocked', {
|
|
342
|
+
adapterId: this.manifest.id,
|
|
343
|
+
violationCount: result.violationCount,
|
|
344
|
+
highestSeverity: result.highestSeverity,
|
|
345
|
+
timestamp: Date.now(),
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return result;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Moderate adapter output before sending
|
|
354
|
+
* @param {Object} response - Response to moderate
|
|
355
|
+
* @param {Object} context - Context
|
|
356
|
+
* @returns {Promise<Object|null>} Moderated response or null if blocked
|
|
357
|
+
*/
|
|
358
|
+
async _moderateResponse(response, context = {}) {
|
|
359
|
+
if (!response) return null;
|
|
360
|
+
|
|
361
|
+
// Extract text content from response
|
|
362
|
+
const textContent = response.content || response.text ||
|
|
363
|
+
(response.card && response.card.title) ||
|
|
364
|
+
'';
|
|
365
|
+
|
|
366
|
+
const result = await this._moderateContent(textContent, context);
|
|
367
|
+
|
|
368
|
+
if (!result.allowed) {
|
|
369
|
+
return {
|
|
370
|
+
type: 'moderation-notice',
|
|
371
|
+
content: 'Content blocked by community standards',
|
|
372
|
+
blocked: true,
|
|
373
|
+
_adapter: {
|
|
374
|
+
id: this.manifest.id,
|
|
375
|
+
moderated: true,
|
|
376
|
+
timestamp: Date.now(),
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return response;
|
|
382
|
+
}
|
|
383
|
+
_verified: true,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Validate manifest against known capabilities
|
|
389
|
+
* @private
|
|
390
|
+
*/
|
|
391
|
+
_validateManifest() {
|
|
392
|
+
const validCaps = new Set(Object.values(CHAT_MOD_CAPABILITIES));
|
|
393
|
+
|
|
394
|
+
for (const cap of this.manifest.capabilities) {
|
|
395
|
+
if (!validCaps.has(cap)) {
|
|
396
|
+
console.warn(\[ChatModAdapter] Unknown capability: \\);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Warn about dangerous combinations
|
|
401
|
+
if (this.manifest.capabilities.has(CHAT_MOD_CAPABILITIES.MSG_READ) &&
|
|
402
|
+
this.manifest.capabilities.has(CHAT_MOD_CAPABILITIES.SPECIAL_DM)) {
|
|
403
|
+
console.warn(\[ChatModAdapter] \ has MSG_READ + SPECIAL_DM - potential privacy concern\);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Check if adapter has a capability
|
|
409
|
+
*/
|
|
410
|
+
hasCapability(capability) {
|
|
411
|
+
return this.manifest.capabilities.has(capability);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Get adapter statistics
|
|
416
|
+
*/
|
|
417
|
+
getStats() {
|
|
418
|
+
return {
|
|
419
|
+
...this.stats,
|
|
420
|
+
manifest: this.manifest.toJSON(),
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Chat Mod Registry - manages installed adapters
|
|
427
|
+
*/
|
|
428
|
+
export class ChatModRegistry extends EventEmitter {
|
|
429
|
+
constructor() {
|
|
430
|
+
super();
|
|
431
|
+
this.adapters = new Map(); // id -> ChatModAdapter
|
|
432
|
+
this.commandMap = new Map(); // command -> adapter id
|
|
433
|
+
this.triggerPatterns = []; // { pattern, adapterId }
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Register an adapter
|
|
438
|
+
* @param {ChatModAdapter} adapter
|
|
439
|
+
*/
|
|
440
|
+
register(adapter) {
|
|
441
|
+
if (this.adapters.has(adapter.manifest.id)) {
|
|
442
|
+
throw new Error(\Adapter already registered: \\);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
this.adapters.set(adapter.manifest.id, adapter);
|
|
446
|
+
|
|
447
|
+
// Register commands
|
|
448
|
+
for (const cmd of adapter.manifest.commands) {
|
|
449
|
+
if (this.commandMap.has(cmd)) {
|
|
450
|
+
console.warn(\Command \/\ already registered by \\);
|
|
451
|
+
} else {
|
|
452
|
+
this.commandMap.set(cmd, adapter.manifest.id);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Register triggers
|
|
457
|
+
for (const trigger of adapter.manifest.triggers) {
|
|
458
|
+
this.triggerPatterns.push({
|
|
459
|
+
pattern: new RegExp(trigger, 'i'),
|
|
460
|
+
adapterId: adapter.manifest.id,
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
this.emit('adapter-registered', adapter.manifest);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Unregister an adapter
|
|
469
|
+
*/
|
|
470
|
+
unregister(adapterId) {
|
|
471
|
+
const adapter = this.adapters.get(adapterId);
|
|
472
|
+
if (!adapter) return;
|
|
473
|
+
|
|
474
|
+
// Remove commands
|
|
475
|
+
for (const cmd of adapter.manifest.commands) {
|
|
476
|
+
if (this.commandMap.get(cmd) === adapterId) {
|
|
477
|
+
this.commandMap.delete(cmd);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Remove triggers
|
|
482
|
+
this.triggerPatterns = this.triggerPatterns.filter(
|
|
483
|
+
t => t.adapterId !== adapterId
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
this.adapters.delete(adapterId);
|
|
487
|
+
this.emit('adapter-unregistered', adapterId);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Route a slash command to the appropriate adapter
|
|
492
|
+
*/
|
|
493
|
+
async routeCommand(command, args, context) {
|
|
494
|
+
const adapterId = this.commandMap.get(command);
|
|
495
|
+
if (!adapterId) return null;
|
|
496
|
+
|
|
497
|
+
const adapter = this.adapters.get(adapterId);
|
|
498
|
+
if (!adapter) return null;
|
|
499
|
+
|
|
500
|
+
return adapter.handleCommand(command, args, context);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Route a message to matching adapters
|
|
505
|
+
*/
|
|
506
|
+
async routeMessage(content, context) {
|
|
507
|
+
const responses = [];
|
|
508
|
+
|
|
509
|
+
for (const { pattern, adapterId } of this.triggerPatterns) {
|
|
510
|
+
if (pattern.test(content)) {
|
|
511
|
+
const adapter = this.adapters.get(adapterId);
|
|
512
|
+
if (adapter) {
|
|
513
|
+
const response = await adapter.handleMessage({ ...context, content });
|
|
514
|
+
if (response) {
|
|
515
|
+
responses.push(response);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return responses;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* List all registered adapters
|
|
526
|
+
*/
|
|
527
|
+
list() {
|
|
528
|
+
return Array.from(this.adapters.values()).map(a => a.manifest.toJSON());
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
export default ChatModAdapter;
|