aiden-runtime 3.16.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 (159) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +465 -0
  3. package/config/devos.config.json +186 -0
  4. package/config/hardware.json +9 -0
  5. package/config/model-selection.json +7 -0
  6. package/config/setup-complete.json +20 -0
  7. package/dist/api/routes/computerUse.js +112 -0
  8. package/dist/api/server.js +6870 -0
  9. package/dist/bin/npx-init.js +71 -0
  10. package/dist/coordination/commandGate.js +115 -0
  11. package/dist/coordination/livePulse.js +127 -0
  12. package/dist/core/agentLoop.js +2718 -0
  13. package/dist/core/agentShield.js +231 -0
  14. package/dist/core/aidenIdentity.js +215 -0
  15. package/dist/core/aidenPersonality.js +166 -0
  16. package/dist/core/aidenSdk.js +374 -0
  17. package/dist/core/asyncTasks.js +82 -0
  18. package/dist/core/auditTrail.js +61 -0
  19. package/dist/core/auxiliaryClient.js +114 -0
  20. package/dist/core/bgLLM.js +108 -0
  21. package/dist/core/bm25.js +68 -0
  22. package/dist/core/callbackSystem.js +64 -0
  23. package/dist/core/channels/adapter.js +6 -0
  24. package/dist/core/channels/discord.js +173 -0
  25. package/dist/core/channels/email.js +253 -0
  26. package/dist/core/channels/imessage.js +164 -0
  27. package/dist/core/channels/manager.js +96 -0
  28. package/dist/core/channels/signal.js +140 -0
  29. package/dist/core/channels/slack.js +139 -0
  30. package/dist/core/channels/twilio.js +144 -0
  31. package/dist/core/channels/webhook.js +186 -0
  32. package/dist/core/channels/whatsapp.js +185 -0
  33. package/dist/core/clarifyBus.js +75 -0
  34. package/dist/core/codeInterpreter.js +82 -0
  35. package/dist/core/computerControl.js +439 -0
  36. package/dist/core/conversationMemory.js +334 -0
  37. package/dist/core/costTracker.js +221 -0
  38. package/dist/core/cronManager.js +217 -0
  39. package/dist/core/deepKB.js +77 -0
  40. package/dist/core/doctor.js +279 -0
  41. package/dist/core/dreamEngine.js +334 -0
  42. package/dist/core/entityGraph.js +169 -0
  43. package/dist/core/eventBus.js +16 -0
  44. package/dist/core/evolutionAnalyzer.js +153 -0
  45. package/dist/core/executionLoop.js +309 -0
  46. package/dist/core/executor.js +224 -0
  47. package/dist/core/failureAnalyzer.js +166 -0
  48. package/dist/core/fastPathExpansion.js +82 -0
  49. package/dist/core/faultEngine.js +106 -0
  50. package/dist/core/featureGates.js +70 -0
  51. package/dist/core/fileIngestion.js +113 -0
  52. package/dist/core/gateway.js +97 -0
  53. package/dist/core/goalTracker.js +75 -0
  54. package/dist/core/growthEngine.js +168 -0
  55. package/dist/core/hardwareDetector.js +98 -0
  56. package/dist/core/hooks.js +45 -0
  57. package/dist/core/httpKeepalive.js +46 -0
  58. package/dist/core/hybridSearch.js +101 -0
  59. package/dist/core/importers.js +164 -0
  60. package/dist/core/instinctSystem.js +223 -0
  61. package/dist/core/knowledgeBase.js +351 -0
  62. package/dist/core/learningMemory.js +121 -0
  63. package/dist/core/lessonsBrowser.js +125 -0
  64. package/dist/core/licenseManager.js +399 -0
  65. package/dist/core/logBuffer.js +85 -0
  66. package/dist/core/machineId.js +87 -0
  67. package/dist/core/mcpClient.js +442 -0
  68. package/dist/core/memoryDistiller.js +165 -0
  69. package/dist/core/memoryExtractor.js +212 -0
  70. package/dist/core/memoryIds.js +213 -0
  71. package/dist/core/memoryPreamble.js +113 -0
  72. package/dist/core/memoryQuery.js +136 -0
  73. package/dist/core/memoryRecall.js +140 -0
  74. package/dist/core/memoryStrategy.js +201 -0
  75. package/dist/core/messageValidator.js +85 -0
  76. package/dist/core/modelDiscovery.js +108 -0
  77. package/dist/core/modelRouter.js +118 -0
  78. package/dist/core/morningBriefing.js +203 -0
  79. package/dist/core/multiGoalValidator.js +51 -0
  80. package/dist/core/parallelExecutor.js +43 -0
  81. package/dist/core/passiveSkillObserver.js +204 -0
  82. package/dist/core/paths.js +57 -0
  83. package/dist/core/patternDetector.js +83 -0
  84. package/dist/core/planResponseRepair.js +64 -0
  85. package/dist/core/planTool.js +111 -0
  86. package/dist/core/playwrightBridge.js +356 -0
  87. package/dist/core/pluginSystem.js +121 -0
  88. package/dist/core/privateMode.js +85 -0
  89. package/dist/core/reactLoop.js +156 -0
  90. package/dist/core/recipeEngine.js +166 -0
  91. package/dist/core/responseCache.js +128 -0
  92. package/dist/core/runSandbox.js +132 -0
  93. package/dist/core/sandboxRunner.js +200 -0
  94. package/dist/core/scheduler.js +543 -0
  95. package/dist/core/secretScanner.js +49 -0
  96. package/dist/core/semanticMemory.js +223 -0
  97. package/dist/core/sessionMemory.js +259 -0
  98. package/dist/core/sessionRouter.js +91 -0
  99. package/dist/core/sessionSearch.js +163 -0
  100. package/dist/core/setupWizard.js +225 -0
  101. package/dist/core/skillImporter.js +303 -0
  102. package/dist/core/skillLibrary.js +144 -0
  103. package/dist/core/skillLoader.js +471 -0
  104. package/dist/core/skillTeacher.js +352 -0
  105. package/dist/core/skillValidator.js +210 -0
  106. package/dist/core/skillWriter.js +384 -0
  107. package/dist/core/slashAsTool.js +226 -0
  108. package/dist/core/spawnManager.js +197 -0
  109. package/dist/core/statusVerbs.js +43 -0
  110. package/dist/core/swarmManager.js +109 -0
  111. package/dist/core/taskQueue.js +119 -0
  112. package/dist/core/taskRecovery.js +128 -0
  113. package/dist/core/taskState.js +168 -0
  114. package/dist/core/telegramBot.js +152 -0
  115. package/dist/core/todoManager.js +70 -0
  116. package/dist/core/toolNameRepair.js +71 -0
  117. package/dist/core/toolRegistry.js +2730 -0
  118. package/dist/core/tools/calendarTool.js +98 -0
  119. package/dist/core/tools/companyFilingsTool.js +98 -0
  120. package/dist/core/tools/gmailTool.js +87 -0
  121. package/dist/core/tools/marketDataTool.js +135 -0
  122. package/dist/core/tools/socialResearchTool.js +121 -0
  123. package/dist/core/truthCheck.js +57 -0
  124. package/dist/core/updateChecker.js +74 -0
  125. package/dist/core/userCognitionProfile.js +238 -0
  126. package/dist/core/userProfile.js +341 -0
  127. package/dist/core/version.js +5 -0
  128. package/dist/core/visionAnalyze.js +161 -0
  129. package/dist/core/voice/audio.js +187 -0
  130. package/dist/core/voice/stt.js +226 -0
  131. package/dist/core/voice/tts.js +310 -0
  132. package/dist/core/voiceInput.js +118 -0
  133. package/dist/core/voiceOutput.js +130 -0
  134. package/dist/core/webSearch.js +326 -0
  135. package/dist/core/workflowTracker.js +72 -0
  136. package/dist/core/workspaceMemory.js +54 -0
  137. package/dist/core/youtubeTranscript.js +224 -0
  138. package/dist/integrations/computerUse/apiRegistry.js +113 -0
  139. package/dist/integrations/computerUse/screenAgent.js +203 -0
  140. package/dist/integrations/computerUse/visionLoop.js +296 -0
  141. package/dist/memory/memoryLayers.js +143 -0
  142. package/dist/providers/boa.js +93 -0
  143. package/dist/providers/cerebras.js +70 -0
  144. package/dist/providers/custom.js +89 -0
  145. package/dist/providers/gemini.js +82 -0
  146. package/dist/providers/groq.js +92 -0
  147. package/dist/providers/index.js +149 -0
  148. package/dist/providers/nvidia.js +70 -0
  149. package/dist/providers/ollama.js +99 -0
  150. package/dist/providers/openrouter.js +74 -0
  151. package/dist/providers/router.js +497 -0
  152. package/dist/providers/types.js +6 -0
  153. package/dist/security/browserVault.js +129 -0
  154. package/dist/security/dataGuard.js +89 -0
  155. package/dist/tools/eonetTool.js +72 -0
  156. package/dist/types/computerUse.js +2 -0
  157. package/dist/types/executor.js +2 -0
  158. package/dist-bundle/cli.js +357859 -0
  159. package/package.json +256 -0
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // DevOS — Autonomous AI Execution System
4
+ // Copyright (c) 2026 Shiva Deore. All rights reserved.
5
+ // ============================================================
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.callBgLLM = callBgLLM;
11
+ // core/bgLLM.ts — Lightweight LLM caller for background agents.
12
+ // Tries Cerebras first (fast, free), falls back to Ollama.
13
+ // All calls tracked as system cost (not counted toward user budget).
14
+ // Separated from agentLoop.ts to avoid circular imports.
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const path_1 = __importDefault(require("path"));
17
+ const costTracker_1 = require("./costTracker");
18
+ const CONFIG_PATH = path_1.default.join(process.cwd(), 'config', 'devos.config.json');
19
+ // ── Config helpers ─────────────────────────────────────────────
20
+ function getCerebrasKey() {
21
+ try {
22
+ const cfg = JSON.parse(fs_1.default.readFileSync(CONFIG_PATH, 'utf-8'));
23
+ const api = (cfg.providers?.apis ?? []).find((a) => a.provider === 'cerebras' && a.enabled);
24
+ if (!api)
25
+ return '';
26
+ const key = api.key;
27
+ return key.startsWith('env:')
28
+ ? (process.env[key.replace('env:', '')] || '')
29
+ : key;
30
+ }
31
+ catch {
32
+ return '';
33
+ }
34
+ }
35
+ function getOllamaModel() {
36
+ try {
37
+ const cfg = JSON.parse(fs_1.default.readFileSync(CONFIG_PATH, 'utf-8'));
38
+ return cfg.model?.activeModel || 'mistral:7b';
39
+ }
40
+ catch {
41
+ return 'mistral:7b';
42
+ }
43
+ }
44
+ // ── callBgLLM ─────────────────────────────────────────────────
45
+ // Simple single-turn LLM call for background agents.
46
+ // Uses Cerebras → Ollama fallback. Always system cost.
47
+ async function callBgLLM(prompt, traceId) {
48
+ // Try Cerebras
49
+ const key = getCerebrasKey();
50
+ if (key) {
51
+ try {
52
+ const r = await fetch('https://api.cerebras.ai/v1/chat/completions', {
53
+ method: 'POST',
54
+ headers: {
55
+ 'Content-Type': 'application/json',
56
+ 'Authorization': `Bearer ${key}`,
57
+ },
58
+ body: JSON.stringify({
59
+ model: 'llama3.1-8b',
60
+ messages: [{ role: 'user', content: prompt }],
61
+ stream: false,
62
+ max_tokens: 2000,
63
+ }),
64
+ signal: AbortSignal.timeout(30000),
65
+ });
66
+ if (r.ok) {
67
+ const d = await r.json();
68
+ const text = d?.choices?.[0]?.message?.content || '';
69
+ const inputTokens = d?.usage?.prompt_tokens ?? 0;
70
+ const outputTokens = d?.usage?.completion_tokens ?? 0;
71
+ try {
72
+ costTracker_1.costTracker.trackUsage('cerebras', 'llama3.1-8b', inputTokens, outputTokens, traceId, true);
73
+ }
74
+ catch { }
75
+ if (text)
76
+ return text;
77
+ }
78
+ }
79
+ catch { }
80
+ }
81
+ // Fallback: Ollama
82
+ try {
83
+ const ollamaModel = getOllamaModel();
84
+ const r = await fetch('http://localhost:11434/api/chat', {
85
+ method: 'POST',
86
+ headers: { 'Content-Type': 'application/json' },
87
+ body: JSON.stringify({
88
+ model: ollamaModel,
89
+ stream: false,
90
+ messages: [{ role: 'user', content: prompt }],
91
+ }),
92
+ signal: AbortSignal.timeout(60000),
93
+ });
94
+ if (r.ok) {
95
+ const d = await r.json();
96
+ const text = d?.message?.content || '';
97
+ try {
98
+ costTracker_1.costTracker.trackUsage('ollama', ollamaModel, d?.prompt_eval_count ?? 0, d?.eval_count ?? 0, traceId, true);
99
+ }
100
+ catch { }
101
+ return text;
102
+ }
103
+ }
104
+ catch (e) {
105
+ console.error('[bgLLM] Ollama fallback failed:', e.message);
106
+ }
107
+ return '';
108
+ }
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // DevOS — Autonomous AI Execution System
4
+ // Copyright (c) 2026 Shiva Deore. All rights reserved.
5
+ // ============================================================
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.BM25 = void 0;
8
+ // core/bm25.ts — Lightweight BM25 ranking. No external dependencies.
9
+ // Okapi BM25 with k1=1.5, b=0.75 (standard defaults).
10
+ class BM25 {
11
+ constructor() {
12
+ this.k1 = 1.5;
13
+ this.b = 0.75;
14
+ this.corpus = [];
15
+ this.idf = new Map();
16
+ this.avgDocLength = 0;
17
+ }
18
+ // ── Index ───────────────────────────────────────────────────
19
+ index(documents) {
20
+ this.corpus = documents;
21
+ const N = documents.length;
22
+ if (N === 0)
23
+ return;
24
+ const df = new Map();
25
+ let totalLength = 0;
26
+ for (const doc of documents) {
27
+ totalLength += doc.split(/\s+/).length;
28
+ const terms = new Set(doc.toLowerCase().split(/\W+/).filter(Boolean));
29
+ for (const term of terms) {
30
+ df.set(term, (df.get(term) || 0) + 1);
31
+ }
32
+ }
33
+ this.avgDocLength = totalLength / N;
34
+ this.idf.clear();
35
+ for (const [term, freq] of df) {
36
+ // Robertson-Walker IDF with +1 smoothing
37
+ this.idf.set(term, Math.log((N - freq + 0.5) / (freq + 0.5) + 1));
38
+ }
39
+ }
40
+ // ── Search ──────────────────────────────────────────────────
41
+ search(query, topK = 5) {
42
+ if (this.corpus.length === 0)
43
+ return [];
44
+ const queryTerms = query.toLowerCase().split(/\W+/).filter(Boolean);
45
+ const scores = new Array(this.corpus.length).fill(0);
46
+ for (const term of queryTerms) {
47
+ const idf = this.idf.get(term);
48
+ if (!idf)
49
+ continue;
50
+ for (let i = 0; i < this.corpus.length; i++) {
51
+ const docTerms = this.corpus[i].toLowerCase().split(/\W+/).filter(Boolean);
52
+ const tf = docTerms.filter(t => t === term).length;
53
+ if (tf === 0)
54
+ continue;
55
+ const docLength = docTerms.length;
56
+ const numerator = tf * (this.k1 + 1);
57
+ const denominator = tf + this.k1 * (1 - this.b + this.b * docLength / this.avgDocLength);
58
+ scores[i] += idf * (numerator / denominator);
59
+ }
60
+ }
61
+ return scores
62
+ .map((score, index) => ({ index, score }))
63
+ .filter(r => r.score > 0)
64
+ .sort((a, b) => b.score - a.score)
65
+ .slice(0, topK);
66
+ }
67
+ }
68
+ exports.BM25 = BM25;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // DevOS — Autonomous AI Execution System
4
+ // Copyright (c) 2026 Shiva Deore. All rights reserved.
5
+ // ============================================================
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.callbacks = void 0;
8
+ // ── Registry ──────────────────────────────────────────────────────────────────
9
+ class CallbackRegistry {
10
+ constructor() {
11
+ this.handlers = new Map();
12
+ this.anyHandlers = [];
13
+ }
14
+ /**
15
+ * Subscribe to a specific event.
16
+ * @returns An unsubscribe function — call it to remove this handler.
17
+ */
18
+ on(event, handler) {
19
+ const list = this.handlers.get(event) ?? [];
20
+ list.push(handler);
21
+ this.handlers.set(event, list);
22
+ return () => {
23
+ const current = this.handlers.get(event) ?? [];
24
+ this.handlers.set(event, current.filter(h => h !== handler));
25
+ };
26
+ }
27
+ /**
28
+ * Subscribe to every event regardless of type.
29
+ * @returns An unsubscribe function.
30
+ */
31
+ onAny(handler) {
32
+ this.anyHandlers.push(handler);
33
+ return () => {
34
+ this.anyHandlers = this.anyHandlers.filter(h => h !== handler);
35
+ };
36
+ }
37
+ /**
38
+ * Emit an event to all matching subscribers.
39
+ * Errors thrown by individual handlers are caught and logged — they
40
+ * never propagate back to the emitter.
41
+ */
42
+ async emit(event, sessionId, data = {}) {
43
+ const payload = {
44
+ event,
45
+ timestamp: Date.now(),
46
+ sessionId,
47
+ data,
48
+ };
49
+ const specific = this.handlers.get(event) ?? [];
50
+ const all = [...specific, ...this.anyHandlers];
51
+ if (all.length === 0)
52
+ return;
53
+ for (const handler of all) {
54
+ try {
55
+ await handler(payload);
56
+ }
57
+ catch (e) {
58
+ console.error(`[Callbacks] Handler error for "${event}": ${e.message}`);
59
+ }
60
+ }
61
+ }
62
+ }
63
+ // ── Singleton ─────────────────────────────────────────────────────────────────
64
+ exports.callbacks = new CallbackRegistry();
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // DevOS — Autonomous AI Execution System
4
+ // Copyright (c) 2026 Shiva Deore. All rights reserved.
5
+ // ============================================================
6
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // DevOS — Autonomous AI Execution System
4
+ // Copyright (c) 2026 Shiva Deore. All rights reserved.
5
+ // ============================================================
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.DiscordAdapter = void 0;
8
+ // core/channels/discord.ts — Discord channel adapter.
9
+ //
10
+ // Config (env vars):
11
+ // DISCORD_BOT_TOKEN — required; adapter stays disabled if absent
12
+ // DISCORD_ALLOWED_GUILDS — optional comma-separated guild IDs
13
+ // DISCORD_ALLOWED_CHANNELS — optional comma-separated channel IDs
14
+ //
15
+ // Features:
16
+ // - Responds to direct messages and guild messages
17
+ // - Slash commands: /aiden <prompt> /aiden-help
18
+ // - Allowlist enforcement for guilds and channels
19
+ // - Ignores messages from other bots (no bot loops)
20
+ // - Graceful degradation: missing token → disabled, no crash
21
+ const discord_js_1 = require("discord.js");
22
+ const gateway_1 = require("../gateway");
23
+ class DiscordAdapter {
24
+ constructor() {
25
+ this.name = 'discord';
26
+ this.client = null;
27
+ this.healthy = false;
28
+ this.token = process.env.DISCORD_BOT_TOKEN ?? '';
29
+ const rawGuilds = process.env.DISCORD_ALLOWED_GUILDS ?? '';
30
+ const rawChannels = process.env.DISCORD_ALLOWED_CHANNELS ?? '';
31
+ this.allowedGuilds = rawGuilds ? new Set(rawGuilds.split(',').map(s => s.trim()).filter(Boolean)) : new Set();
32
+ this.allowedChannels = rawChannels ? new Set(rawChannels.split(',').map(s => s.trim()).filter(Boolean)) : new Set();
33
+ }
34
+ // ── Lifecycle ──────────────────────────────────────────────
35
+ async start() {
36
+ if (!this.token) {
37
+ console.log('[Discord] Disabled — set DISCORD_BOT_TOKEN to enable');
38
+ return;
39
+ }
40
+ this.client = new discord_js_1.Client({
41
+ intents: [
42
+ discord_js_1.GatewayIntentBits.Guilds,
43
+ discord_js_1.GatewayIntentBits.GuildMessages,
44
+ discord_js_1.GatewayIntentBits.MessageContent,
45
+ discord_js_1.GatewayIntentBits.DirectMessages,
46
+ ],
47
+ });
48
+ this.client.once(discord_js_1.Events.ClientReady, async (c) => {
49
+ console.log(`[Discord] Connected as ${c.user.tag}`);
50
+ this.healthy = true;
51
+ // Register outbound delivery so gateway.deliver() and broadcast() work
52
+ gateway_1.gateway.registerChannel('discord', async (msg) => {
53
+ return this.deliverToChannel(msg.channelId, msg.text);
54
+ });
55
+ // Register slash commands globally (takes ~1h to propagate on first run)
56
+ await this.registerSlashCommands(c.user.id).catch((e) => console.warn('[Discord] Slash command registration failed:', e.message));
57
+ });
58
+ this.client.on(discord_js_1.Events.MessageCreate, async (message) => {
59
+ if (!this.shouldHandle(message.author.id, message.guildId, message.channelId, message.author.bot))
60
+ return;
61
+ try {
62
+ await message.channel.sendTyping?.();
63
+ }
64
+ catch { }
65
+ const response = await this.processMessage(message.channelId, message.author.id, message.content);
66
+ await message.reply(response.substring(0, 2000)).catch((e) => console.error('[Discord] Reply error:', e.message));
67
+ });
68
+ this.client.on(discord_js_1.Events.InteractionCreate, async (interaction) => {
69
+ if (!interaction.isChatInputCommand())
70
+ return;
71
+ const guildId = interaction.guildId;
72
+ const channelId = interaction.channelId;
73
+ const userId = interaction.user.id;
74
+ // Allowlist check
75
+ if (this.allowedGuilds.size > 0 && guildId && !this.allowedGuilds.has(guildId)) {
76
+ await interaction.reply({ content: '⚠️ This server is not authorized.', ephemeral: true });
77
+ return;
78
+ }
79
+ if (this.allowedChannels.size > 0 && !this.allowedChannels.has(channelId)) {
80
+ await interaction.reply({ content: '⚠️ This channel is not authorized.', ephemeral: true });
81
+ return;
82
+ }
83
+ if (interaction.commandName === 'aiden') {
84
+ const prompt = interaction.options.getString('prompt', true);
85
+ await interaction.deferReply();
86
+ const response = await this.processMessage(channelId, userId, prompt);
87
+ await interaction.editReply(response.substring(0, 2000)).catch((e) => console.error('[Discord] editReply error:', e.message));
88
+ }
89
+ else if (interaction.commandName === 'aiden-help') {
90
+ await interaction.reply({
91
+ content: '**Aiden** — your local AI assistant\n\n`/aiden <prompt>` — ask anything\n`/aiden-help` — show this message',
92
+ ephemeral: true,
93
+ });
94
+ }
95
+ });
96
+ try {
97
+ await this.client.login(this.token);
98
+ }
99
+ catch (e) {
100
+ console.error('[Discord] Login failed:', e.message);
101
+ this.healthy = false;
102
+ }
103
+ }
104
+ async stop() {
105
+ this.healthy = false;
106
+ if (this.client) {
107
+ gateway_1.gateway.unregisterChannel('discord');
108
+ await this.client.destroy();
109
+ this.client = null;
110
+ }
111
+ console.log('[Discord] Disconnected');
112
+ }
113
+ async send(channelId, message) {
114
+ await this.deliverToChannel(channelId, message);
115
+ }
116
+ isHealthy() { return this.healthy; }
117
+ // ── Helpers ────────────────────────────────────────────────
118
+ shouldHandle(authorId, guildId, channelId, isBot) {
119
+ if (isBot)
120
+ return false;
121
+ if (this.allowedGuilds.size > 0 && guildId && !this.allowedGuilds.has(guildId))
122
+ return false;
123
+ if (this.allowedChannels.size > 0 && !this.allowedChannels.has(channelId))
124
+ return false;
125
+ return true;
126
+ }
127
+ async processMessage(channelId, userId, text) {
128
+ try {
129
+ return await gateway_1.gateway.routeMessage({
130
+ channel: 'discord',
131
+ channelId,
132
+ userId,
133
+ text,
134
+ timestamp: Date.now(),
135
+ });
136
+ }
137
+ catch (e) {
138
+ console.error('[Discord] routeMessage error:', e.message);
139
+ return '❌ Something went wrong. Try again.';
140
+ }
141
+ }
142
+ async deliverToChannel(channelId, text) {
143
+ try {
144
+ const ch = this.client?.channels.cache.get(channelId);
145
+ if (ch && (ch instanceof discord_js_1.TextChannel || ch instanceof discord_js_1.DMChannel)) {
146
+ await ch.send(text.substring(0, 2000));
147
+ return true;
148
+ }
149
+ return false;
150
+ }
151
+ catch (e) {
152
+ console.error('[Discord] Delivery error:', e.message);
153
+ return false;
154
+ }
155
+ }
156
+ async registerSlashCommands(appId) {
157
+ const rest = new discord_js_1.REST({ version: '10' }).setToken(this.token);
158
+ const commands = [
159
+ new discord_js_1.SlashCommandBuilder()
160
+ .setName('aiden')
161
+ .setDescription('Ask Aiden anything')
162
+ .addStringOption(opt => opt.setName('prompt').setDescription('Your message to Aiden').setRequired(true))
163
+ .toJSON(),
164
+ new discord_js_1.SlashCommandBuilder()
165
+ .setName('aiden-help')
166
+ .setDescription('Show Aiden capabilities')
167
+ .toJSON(),
168
+ ];
169
+ await rest.put(discord_js_1.Routes.applicationCommands(appId), { body: commands });
170
+ console.log('[Discord] Slash commands registered globally');
171
+ }
172
+ }
173
+ exports.DiscordAdapter = DiscordAdapter;
@@ -0,0 +1,253 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // DevOS — Autonomous AI Execution System
4
+ // Copyright (c) 2026 Shiva Deore. All rights reserved.
5
+ // ============================================================
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.EmailAdapter = void 0;
11
+ // core/channels/email.ts — Email-as-interface channel adapter.
12
+ //
13
+ // Aiden acts as an email assistant: users send messages TO a dedicated
14
+ // inbox and Aiden replies via SMTP. Configure a Gmail, Fastmail, or any
15
+ // IMAP/SMTP-capable account. Use an app-specific password for Gmail.
16
+ //
17
+ // Loop prevention:
18
+ // - Skips messages where From matches EMAIL_SMTP_USER (self-sent)
19
+ // - Skips messages that already have an X-Aiden-Reply header
20
+ // - Only processes subjects that don't start with "Re: " already
21
+ // replied by Aiden
22
+ //
23
+ // Config (env vars):
24
+ // EMAIL_IMAP_HOST — e.g. imap.gmail.com
25
+ // EMAIL_IMAP_PORT — default 993 (TLS)
26
+ // EMAIL_IMAP_USER — your email address
27
+ // EMAIL_IMAP_PASSWORD — account or app-specific password
28
+ // EMAIL_SMTP_HOST — e.g. smtp.gmail.com
29
+ // EMAIL_SMTP_PORT — default 587 (STARTTLS)
30
+ // EMAIL_SMTP_USER — usually same as IMAP user
31
+ // EMAIL_SMTP_PASSWORD — usually same as IMAP password
32
+ // EMAIL_ALLOWED_SENDERS — optional comma-separated from-address allowlist
33
+ // EMAIL_POLL_INTERVAL — polling interval in seconds (default 60)
34
+ const nodemailer_1 = __importDefault(require("nodemailer"));
35
+ const gateway_1 = require("../gateway");
36
+ class EmailAdapter {
37
+ constructor() {
38
+ this.name = 'email';
39
+ this.healthy = false;
40
+ this.pollTimer = null;
41
+ this.processedIds = new Set();
42
+ this.transporter = null;
43
+ this.imapHost = process.env.EMAIL_IMAP_HOST ?? '';
44
+ this.imapPort = parseInt(process.env.EMAIL_IMAP_PORT ?? '993', 10);
45
+ this.imapUser = process.env.EMAIL_IMAP_USER ?? '';
46
+ this.imapPassword = process.env.EMAIL_IMAP_PASSWORD ?? '';
47
+ this.smtpHost = process.env.EMAIL_SMTP_HOST ?? '';
48
+ this.smtpPort = parseInt(process.env.EMAIL_SMTP_PORT ?? '587', 10);
49
+ this.smtpUser = process.env.EMAIL_SMTP_USER ?? '';
50
+ this.smtpPassword = process.env.EMAIL_SMTP_PASSWORD ?? '';
51
+ const raw = process.env.EMAIL_ALLOWED_SENDERS ?? '';
52
+ this.allowedSenders = raw ? new Set(raw.split(',').map(s => s.trim().toLowerCase()).filter(Boolean)) : new Set();
53
+ this.pollIntervalMs = parseInt(process.env.EMAIL_POLL_INTERVAL ?? '60', 10) * 1000;
54
+ }
55
+ // ── Lifecycle ──────────────────────────────────────────────
56
+ async start() {
57
+ if (!this.imapHost || !this.imapUser || !this.imapPassword) {
58
+ console.log('[Email] Disabled — set EMAIL_IMAP_HOST, EMAIL_IMAP_USER, EMAIL_IMAP_PASSWORD to enable');
59
+ return;
60
+ }
61
+ if (!this.smtpHost || !this.smtpUser || !this.smtpPassword) {
62
+ console.log('[Email] Disabled — set EMAIL_SMTP_HOST, EMAIL_SMTP_USER, EMAIL_SMTP_PASSWORD to enable');
63
+ return;
64
+ }
65
+ // Set up SMTP transporter
66
+ this.transporter = nodemailer_1.default.createTransport({
67
+ host: this.smtpHost,
68
+ port: this.smtpPort,
69
+ secure: this.smtpPort === 465,
70
+ auth: {
71
+ user: this.smtpUser,
72
+ pass: this.smtpPassword,
73
+ },
74
+ });
75
+ // Verify SMTP connection
76
+ const smtpOk = await this.transporter.verify().then(() => true).catch(() => false);
77
+ if (!smtpOk) {
78
+ console.log('[Email] Disabled — SMTP connection failed. Check EMAIL_SMTP_* settings.');
79
+ return;
80
+ }
81
+ this.healthy = true;
82
+ console.log(`[Email] Ready — polling ${this.imapUser} every ${this.pollIntervalMs / 1000}s`);
83
+ // Register outbound delivery
84
+ gateway_1.gateway.registerChannel('email', async (msg) => {
85
+ await this.send(msg.channelId, msg.text);
86
+ return true;
87
+ });
88
+ // Initial poll then start interval
89
+ await this.poll();
90
+ this.pollTimer = setInterval(() => this.poll(), this.pollIntervalMs);
91
+ }
92
+ async stop() {
93
+ this.healthy = false;
94
+ if (this.pollTimer) {
95
+ clearInterval(this.pollTimer);
96
+ this.pollTimer = null;
97
+ }
98
+ this.transporter = null;
99
+ gateway_1.gateway.unregisterChannel('email');
100
+ console.log('[Email] Disconnected');
101
+ }
102
+ async send(target, message) {
103
+ if (!this.transporter)
104
+ return;
105
+ try {
106
+ await this.transporter.sendMail({
107
+ from: this.smtpUser,
108
+ to: target,
109
+ subject: 'Message from Aiden',
110
+ text: message,
111
+ headers: { 'X-Aiden-Reply': '1' },
112
+ });
113
+ }
114
+ catch (e) {
115
+ console.error('[Email] send error:', e.message);
116
+ }
117
+ }
118
+ isHealthy() { return this.healthy; }
119
+ // ── Polling ────────────────────────────────────────────────
120
+ async poll() {
121
+ if (!this.healthy)
122
+ return;
123
+ let imapSimple;
124
+ try {
125
+ imapSimple = require('imap-simple');
126
+ }
127
+ catch {
128
+ return;
129
+ }
130
+ let connection = null;
131
+ try {
132
+ const config = {
133
+ imap: {
134
+ host: this.imapHost,
135
+ port: this.imapPort,
136
+ tls: true,
137
+ tlsOptions: { rejectUnauthorized: false },
138
+ user: this.imapUser,
139
+ password: this.imapPassword,
140
+ authTimeout: 5000,
141
+ },
142
+ };
143
+ connection = await imapSimple.connect(config);
144
+ await connection.openBox('INBOX');
145
+ // Fetch unseen messages
146
+ const messages = await connection.search(['UNSEEN'], { bodies: ['HEADER', 'TEXT'], markSeen: false });
147
+ for (const item of messages) {
148
+ const all = item.parts.find((p) => p.which === 'TEXT');
149
+ const header = item.parts.find((p) => p.which === 'HEADER');
150
+ const msgId = item.attributes.uid?.toString() ?? '';
151
+ if (this.processedIds.has(msgId))
152
+ continue;
153
+ const headers = imapSimple.getParts ? {} : (header?.body ?? {});
154
+ const fromRaw = (headers['from']?.[0] ?? '').toLowerCase();
155
+ const subject = (headers['subject']?.[0] ?? '');
156
+ const aidenHdr = headers['x-aiden-reply']?.[0];
157
+ // ── Loop prevention ──────────────────────────────────
158
+ // 1. Skip messages from ourselves
159
+ if (fromRaw.includes(this.smtpUser.toLowerCase())) {
160
+ this.processedIds.add(msgId);
161
+ continue;
162
+ }
163
+ // 2. Skip if already tagged as an Aiden reply
164
+ if (aidenHdr) {
165
+ this.processedIds.add(msgId);
166
+ continue;
167
+ }
168
+ // Extract sender email
169
+ const senderMatch = fromRaw.match(/<([^>]+)>/) ?? [null, fromRaw.trim()];
170
+ const senderEmail = senderMatch[1] ?? fromRaw.trim();
171
+ if (!this.isAllowed(senderEmail)) {
172
+ this.processedIds.add(msgId);
173
+ continue;
174
+ }
175
+ const body = all?.body ?? '';
176
+ if (!body.trim()) {
177
+ this.processedIds.add(msgId);
178
+ continue;
179
+ }
180
+ this.processedIds.add(msgId);
181
+ // Route message to agent
182
+ const response = await this.processMessage(senderEmail, senderEmail, this.extractText(body));
183
+ // Reply via SMTP
184
+ await this.replyEmail(senderEmail, subject, response);
185
+ // Mark message as seen
186
+ try {
187
+ await connection.addFlags(item.attributes.uid, '\\Seen');
188
+ }
189
+ catch { }
190
+ }
191
+ }
192
+ catch (e) {
193
+ if (this.healthy) {
194
+ console.error('[Email] poll error:', e.message);
195
+ }
196
+ }
197
+ finally {
198
+ if (connection) {
199
+ try {
200
+ connection.end();
201
+ }
202
+ catch { }
203
+ }
204
+ }
205
+ }
206
+ async replyEmail(to, originalSubject, body) {
207
+ if (!this.transporter)
208
+ return;
209
+ const subject = originalSubject.startsWith('Re:') ? originalSubject : `Re: ${originalSubject}`;
210
+ try {
211
+ await this.transporter.sendMail({
212
+ from: this.smtpUser,
213
+ to,
214
+ subject,
215
+ text: body,
216
+ headers: { 'X-Aiden-Reply': '1' },
217
+ });
218
+ }
219
+ catch (e) {
220
+ console.error('[Email] reply error:', e.message);
221
+ }
222
+ }
223
+ extractText(raw) {
224
+ // Strip quoted reply sections and HTML tags for clean prompt
225
+ return raw
226
+ .replace(/<[^>]+>/g, '') // remove HTML tags
227
+ .replace(/^>.*$/gm, '') // remove email quote lines
228
+ .replace(/\r\n/g, '\n')
229
+ .trim()
230
+ .substring(0, 4000); // cap at 4KB
231
+ }
232
+ isAllowed(email) {
233
+ if (this.allowedSenders.size === 0)
234
+ return true;
235
+ return this.allowedSenders.has(email.toLowerCase());
236
+ }
237
+ async processMessage(channelId, userId, text) {
238
+ try {
239
+ return await gateway_1.gateway.routeMessage({
240
+ channel: 'email',
241
+ channelId,
242
+ userId,
243
+ text,
244
+ timestamp: Date.now(),
245
+ });
246
+ }
247
+ catch (e) {
248
+ console.error('[Email] routeMessage error:', e.message);
249
+ return 'Something went wrong processing your email. Please try again.';
250
+ }
251
+ }
252
+ }
253
+ exports.EmailAdapter = EmailAdapter;