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,223 @@
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.semanticMemory = exports.SemanticMemory = void 0;
11
+ // core/semanticMemory.ts — Local semantic memory with TF-IDF inspired
12
+ // word-level embeddings. No external API required — pure JS math.
13
+ const fs_1 = __importDefault(require("fs"));
14
+ const path_1 = __importDefault(require("path"));
15
+ const bm25_1 = require("./bm25");
16
+ const MEMORY_PATH = path_1.default.join(process.cwd(), 'workspace', 'semantic.json');
17
+ // ── Temporal decay helpers ────────────────────────────────────
18
+ const HALF_LIFE_DAYS = 30;
19
+ const DECAY_LAMBDA = Math.LN2 / HALF_LIFE_DAYS;
20
+ function applyTemporalDecay(results) {
21
+ const now = Date.now();
22
+ return results.map(r => {
23
+ // Facts and entities are evergreen — never decay
24
+ if (r.item.metadata.type === 'fact' || r.item.metadata.type === 'entity') {
25
+ return r;
26
+ }
27
+ const ageInDays = (now - r.item.metadata.timestamp) / 86400000;
28
+ const multiplier = Math.exp(-DECAY_LAMBDA * ageInDays);
29
+ return { ...r, score: r.score * multiplier };
30
+ }).sort((a, b) => b.score - a.score);
31
+ }
32
+ // ── MMR diversity re-ranking helpers ──────────────────────────
33
+ function tokenize(text) {
34
+ return new Set(text.toLowerCase().split(/\W+/).filter(t => t.length > 2));
35
+ }
36
+ function jaccardSimilarity(a, b) {
37
+ const intersection = new Set([...a].filter(x => b.has(x)));
38
+ const union = new Set([...a, ...b]);
39
+ return union.size === 0 ? 0 : intersection.size / union.size;
40
+ }
41
+ function applyMMR(results, lambda = 0.7, maxResults = 10) {
42
+ if (results.length <= 1)
43
+ return results;
44
+ const selected = [];
45
+ const remaining = [...results];
46
+ // Always pick highest-scoring first
47
+ selected.push(remaining.shift());
48
+ while (selected.length < maxResults && remaining.length > 0) {
49
+ let bestIdx = 0;
50
+ let bestScore = -Infinity;
51
+ for (let i = 0; i < remaining.length; i++) {
52
+ const relevance = remaining[i].score;
53
+ const maxSim = Math.max(...selected.map(s => jaccardSimilarity(tokenize(remaining[i].item.text), tokenize(s.item.text))));
54
+ const mmrScore = lambda * relevance - (1 - lambda) * maxSim;
55
+ if (mmrScore > bestScore) {
56
+ bestScore = mmrScore;
57
+ bestIdx = i;
58
+ }
59
+ }
60
+ selected.push(remaining.splice(bestIdx, 1)[0]);
61
+ }
62
+ return selected;
63
+ }
64
+ class SemanticMemory {
65
+ constructor() {
66
+ this.data = [];
67
+ this.bm25 = new bm25_1.BM25();
68
+ this.bm25IndexBuilt = false;
69
+ this.load();
70
+ }
71
+ // ── Persistence ───────────────────────────────────────────────
72
+ load() {
73
+ try {
74
+ if (fs_1.default.existsSync(MEMORY_PATH)) {
75
+ this.data = JSON.parse(fs_1.default.readFileSync(MEMORY_PATH, 'utf-8'));
76
+ }
77
+ }
78
+ catch { }
79
+ }
80
+ save() {
81
+ try {
82
+ fs_1.default.mkdirSync(path_1.default.dirname(MEMORY_PATH), { recursive: true });
83
+ fs_1.default.writeFileSync(MEMORY_PATH, JSON.stringify(this.data, null, 2));
84
+ }
85
+ catch { }
86
+ }
87
+ // ── Embedding ─────────────────────────────────────────────────
88
+ // TF-IDF inspired bag-of-words embedding into 128-dim space.
89
+ // Uses polynomial rolling hash — similar topics yield similar vectors.
90
+ embed(text) {
91
+ const dim = 128;
92
+ const vec = new Array(dim).fill(0);
93
+ // Normalize and tokenize
94
+ const words = text
95
+ .toLowerCase()
96
+ .replace(/[^a-z0-9\s]/g, ' ')
97
+ .split(/\s+/)
98
+ .filter(w => w.length > 2);
99
+ // Remove stop words
100
+ const stopWords = new Set([
101
+ 'the', 'and', 'for', 'are', 'but', 'not', 'you', 'all', 'can',
102
+ 'her', 'was', 'one', 'our', 'out', 'day', 'get', 'has', 'him',
103
+ 'his', 'how', 'its', 'may', 'new', 'now', 'old', 'see', 'two',
104
+ 'way', 'who', 'did', 'let', 'put', 'say', 'she', 'too', 'use',
105
+ ]);
106
+ const meaningful = words.filter(w => !stopWords.has(w));
107
+ // Hash each word into vector space using polynomial rolling hash
108
+ for (let wi = 0; wi < meaningful.length; wi++) {
109
+ const word = meaningful[wi];
110
+ let h1 = 0, h2 = 0;
111
+ for (let i = 0; i < word.length; i++) {
112
+ const c = word.charCodeAt(i);
113
+ h1 = (h1 * 31 + c) % dim;
114
+ h2 = (h2 * 37 + c) % dim;
115
+ }
116
+ // Boost by word length — longer words carry more meaning
117
+ const weight = Math.log(word.length + 1);
118
+ vec[h1] += weight;
119
+ vec[h2] += weight * 0.5;
120
+ // Bigram context — captures phrase meaning
121
+ if (wi > 0) {
122
+ const prev = meaningful[wi - 1];
123
+ let bh = 0;
124
+ for (let i = 0; i < prev.length && i < 4; i++) {
125
+ bh = (bh * 41 + prev.charCodeAt(i)) % dim;
126
+ }
127
+ vec[(h1 + bh) % dim] += 0.3;
128
+ }
129
+ }
130
+ // L2 normalize
131
+ const mag = Math.sqrt(vec.reduce((s, v) => s + v * v, 0)) + 1e-8;
132
+ return vec.map(v => v / mag);
133
+ }
134
+ cosine(a, b) {
135
+ let dot = 0;
136
+ for (let i = 0; i < a.length; i++)
137
+ dot += a[i] * b[i];
138
+ return Math.max(0, Math.min(1, dot)); // already L2-normalized
139
+ }
140
+ // ── Public API ────────────────────────────────────────────────
141
+ add(text, type = 'exchange', tags) {
142
+ if (!text.trim() || text.length < 10)
143
+ return;
144
+ // Don't store duplicates
145
+ const existing = this.data.find(d => d.text === text);
146
+ if (existing)
147
+ return;
148
+ const item = {
149
+ id: `mem_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
150
+ text: text.slice(0, 500),
151
+ embedding: this.embed(text),
152
+ metadata: { type, timestamp: Date.now(), tags },
153
+ };
154
+ this.data.push(item);
155
+ this.bm25IndexBuilt = false; // Sprint 14: invalidate BM25 index on new item
156
+ // Keep max 500 items — remove oldest
157
+ if (this.data.length > 500) {
158
+ this.data = this.data
159
+ .sort((a, b) => b.metadata.timestamp - a.metadata.timestamp)
160
+ .slice(0, 500);
161
+ }
162
+ this.save();
163
+ }
164
+ // ── BM25 index builder ────────────────────────────────────────
165
+ buildBM25Index() {
166
+ const texts = this.data.map(d => d.text);
167
+ if (texts.length > 0) {
168
+ this.bm25.index(texts);
169
+ this.bm25IndexBuilt = true;
170
+ }
171
+ }
172
+ // ── Vector-only search ────────────────────────────────────────
173
+ vectorSearch(query, topK) {
174
+ const qVec = this.embed(query);
175
+ return this.data
176
+ .map((item, index) => ({ index, score: this.cosine(qVec, item.embedding) }))
177
+ .filter(r => r.score > 0)
178
+ .sort((a, b) => b.score - a.score)
179
+ .slice(0, topK);
180
+ }
181
+ // ── Hybrid search: BM25 + vector with reciprocal rank fusion ──
182
+ search(query, topK = 5, minScore = 0.3) {
183
+ if (this.data.length === 0)
184
+ return [];
185
+ if (!this.bm25IndexBuilt)
186
+ this.buildBM25Index();
187
+ const fetch = topK * 2;
188
+ // BM25 reciprocal rank scores (weight 0.4)
189
+ const bm25Results = this.bm25.search(query, fetch);
190
+ const bm25Scores = new Map();
191
+ bm25Results.forEach((r, rank) => bm25Scores.set(r.index, 1 / (rank + 1)));
192
+ // Vector reciprocal rank scores (weight 0.6)
193
+ const vectorResults = this.vectorSearch(query, fetch);
194
+ const vectorScores = new Map();
195
+ vectorResults.forEach((r, rank) => vectorScores.set(r.index, 1 / (rank + 1)));
196
+ // Reciprocal rank fusion
197
+ const allIndices = new Set([...bm25Scores.keys(), ...vectorScores.keys()]);
198
+ const fused = Array.from(allIndices)
199
+ .map(idx => ({
200
+ item: this.data[idx],
201
+ score: (bm25Scores.get(idx) || 0) * 0.4 + (vectorScores.get(idx) || 0) * 0.6,
202
+ }))
203
+ .filter(r => r.item && r.score >= minScore)
204
+ .sort((a, b) => b.score - a.score);
205
+ // Feature 4: temporal decay — recent memories rank higher
206
+ const decayed = applyTemporalDecay(fused);
207
+ // Feature 5: MMR diversity — prevent duplicate daily-note results
208
+ const diverse = applyMMR(decayed, 0.7, topK);
209
+ return diverse.map(r => r.item);
210
+ }
211
+ searchText(query, topK = 3) {
212
+ return this.search(query, topK).map(item => item.text);
213
+ }
214
+ getStats() {
215
+ const byType = {};
216
+ this.data.forEach(d => {
217
+ byType[d.metadata.type] = (byType[d.metadata.type] || 0) + 1;
218
+ });
219
+ return { total: this.data.length, byType };
220
+ }
221
+ }
222
+ exports.SemanticMemory = SemanticMemory;
223
+ exports.semanticMemory = new SemanticMemory();
@@ -0,0 +1,259 @@
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.sessionMemory = void 0;
11
+ exports.saveSessionMetadata = saveSessionMetadata;
12
+ exports.loadSessionMetadata = loadSessionMetadata;
13
+ exports.getSessionLineage = getSessionLineage;
14
+ exports.createChildSession = createChildSession;
15
+ // core/sessionMemory.ts — Per-session markdown log.
16
+ // After every 5 exchanges AND on conversation end, a background
17
+ // agent writes/updates workspace/sessions/{sessionId}.md.
18
+ // Injects last session context at conversation start.
19
+ const fs_1 = __importDefault(require("fs"));
20
+ const path_1 = __importDefault(require("path"));
21
+ const auxiliaryClient_1 = require("./auxiliaryClient");
22
+ const SESSIONS_DIR = path_1.default.join(process.env.AIDEN_USER_DATA || process.cwd(), 'workspace', 'sessions');
23
+ function metaPath(sessionId) {
24
+ return path_1.default.join(SESSIONS_DIR, `${sessionId}.meta.json`);
25
+ }
26
+ function saveSessionMetadata(meta) {
27
+ try {
28
+ fs_1.default.mkdirSync(SESSIONS_DIR, { recursive: true });
29
+ fs_1.default.writeFileSync(metaPath(meta.id), JSON.stringify(meta, null, 2), 'utf-8');
30
+ }
31
+ catch (e) {
32
+ console.error('[SessionLineage] Save failed:', e.message);
33
+ }
34
+ }
35
+ function loadSessionMetadata(sessionId) {
36
+ try {
37
+ const p = metaPath(sessionId);
38
+ if (!fs_1.default.existsSync(p))
39
+ return null;
40
+ return JSON.parse(fs_1.default.readFileSync(p, 'utf-8'));
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ function getSessionLineage(sessionId) {
47
+ const lineage = [];
48
+ let current = loadSessionMetadata(sessionId);
49
+ if (!current)
50
+ return lineage;
51
+ const visited = new Set();
52
+ // Walk back to root via parentId chain
53
+ while (current && !visited.has(current.id)) {
54
+ visited.add(current.id);
55
+ lineage.unshift(current);
56
+ if (!current.parentId)
57
+ break;
58
+ current = loadSessionMetadata(current.parentId);
59
+ }
60
+ // Walk forward from tail to include any children not yet visited
61
+ let tail = lineage[lineage.length - 1];
62
+ while (tail?.childId && !visited.has(tail.childId)) {
63
+ visited.add(tail.childId);
64
+ const child = loadSessionMetadata(tail.childId);
65
+ if (!child)
66
+ break;
67
+ lineage.push(child);
68
+ tail = child;
69
+ }
70
+ return lineage;
71
+ }
72
+ function createChildSession(parentSessionId, reason, messageCount, tokenCount) {
73
+ const childId = `session_${Date.now()}`;
74
+ const now = Date.now();
75
+ // Load or bootstrap parent metadata
76
+ const parentMeta = loadSessionMetadata(parentSessionId) ?? {
77
+ id: parentSessionId,
78
+ depth: 0,
79
+ originalSessionId: parentSessionId,
80
+ createdAt: now,
81
+ };
82
+ // Stamp compression info on parent
83
+ parentMeta.childId = childId;
84
+ parentMeta.compressedAt = now;
85
+ parentMeta.messageCountBeforeCompression = messageCount;
86
+ parentMeta.tokenCountBeforeCompression = tokenCount;
87
+ saveSessionMetadata(parentMeta);
88
+ // Create child metadata
89
+ const childMeta = {
90
+ id: childId,
91
+ parentId: parentSessionId,
92
+ depth: parentMeta.depth + 1,
93
+ compressionReason: reason,
94
+ originalSessionId: parentMeta.originalSessionId,
95
+ createdAt: now,
96
+ };
97
+ saveSessionMetadata(childMeta);
98
+ console.log(`[SessionLineage] Created child ${childId} (depth ${childMeta.depth}) from ${parentSessionId}`);
99
+ return childId;
100
+ }
101
+ // ── Section headers (must match exactly) ──────────────────────
102
+ const SECTION_HEADERS = [
103
+ '# Session Title',
104
+ '# Current State',
105
+ '# What the User Asked',
106
+ '# Files Touched',
107
+ '# Commands Run',
108
+ '# Errors & Fixes',
109
+ '# Learnings',
110
+ '# Results',
111
+ '# Worklog',
112
+ ];
113
+ // ── Helpers ───────────────────────────────────────────────────
114
+ function sessionPath(sessionId) {
115
+ return path_1.default.join(SESSIONS_DIR, `${sessionId}.md`);
116
+ }
117
+ function buildPrompt(state) {
118
+ const msgs = state.exchanges.map(e => `[${e.role.toUpperCase()}] ${e.content.slice(0, 600)}`).join('\n\n');
119
+ const files = state.exchanges.flatMap(e => e.files ?? []).filter(Boolean);
120
+ const cmds = state.exchanges.flatMap(e => e.cmds ?? []).filter(Boolean);
121
+ const errors = state.exchanges.flatMap(e => e.errors ?? []).filter(Boolean);
122
+ return `You are a session memory writer for DevOS. Based on the conversation below, write a session summary in this exact format with these exact headers (never change the headers):
123
+
124
+ # Session Title
125
+ _A short 5-10 word title_
126
+
127
+ # Current State
128
+ _What's being worked on right now? Next steps?_
129
+
130
+ # What the User Asked
131
+ _The original goal. Design decisions made._
132
+
133
+ # Files Touched
134
+ _Important files and what was done to them._
135
+
136
+ # Commands Run
137
+ _Key commands and their purpose._
138
+
139
+ # Errors & Fixes
140
+ _What broke and how it was resolved._
141
+
142
+ # Learnings
143
+ _What worked, what didn't, what to avoid next time._
144
+
145
+ # Results
146
+ _Key outputs or answers produced._
147
+
148
+ # Worklog
149
+ _Terse step-by-step of what happened._
150
+
151
+ CONVERSATION:
152
+ ${msgs}
153
+
154
+ FILES TOUCHED: ${files.join(', ') || 'none'}
155
+ COMMANDS RUN: ${cmds.join(', ') || 'none'}
156
+ ERRORS: ${errors.join(', ') || 'none'}
157
+
158
+ Write the session summary now. Use the exact headers shown above.`;
159
+ }
160
+ // ── SessionMemory ─────────────────────────────────────────────
161
+ class SessionMemory {
162
+ constructor() {
163
+ this.sessions = new Map();
164
+ try {
165
+ fs_1.default.mkdirSync(SESSIONS_DIR, { recursive: true });
166
+ }
167
+ catch { }
168
+ }
169
+ // ── Start / get session ───────────────────────────────────
170
+ getOrCreate(sessionId) {
171
+ if (!this.sessions.has(sessionId)) {
172
+ this.sessions.set(sessionId, {
173
+ sessionId,
174
+ startTs: Date.now(),
175
+ exchanges: [],
176
+ });
177
+ }
178
+ return this.sessions.get(sessionId);
179
+ }
180
+ // ── Add exchange ──────────────────────────────────────────
181
+ addExchange(sessionId, userMsg, aiMsg, files, cmds, errors) {
182
+ const state = this.getOrCreate(sessionId);
183
+ state.exchanges.push({ role: 'user', content: userMsg, ts: Date.now() });
184
+ state.exchanges.push({ role: 'assistant', content: aiMsg, ts: Date.now(), files, cmds, errors });
185
+ // Every 5 exchange pairs (10 messages), write in background
186
+ const pairs = Math.floor(state.exchanges.length / 2);
187
+ if (pairs > 0 && pairs % 5 === 0) {
188
+ setTimeout(() => { this.writeSession(sessionId).catch(() => { }); }, 100);
189
+ }
190
+ }
191
+ // ── End session ───────────────────────────────────────────
192
+ endSession(sessionId) {
193
+ setTimeout(() => { this.writeSession(sessionId).catch(() => { }); }, 100);
194
+ }
195
+ // ── Write session file ────────────────────────────────────
196
+ async writeSession(sessionId) {
197
+ const state = this.sessions.get(sessionId);
198
+ if (!state || state.exchanges.length === 0)
199
+ return;
200
+ try {
201
+ const prompt = buildPrompt(state);
202
+ const content = await auxiliaryClient_1.auxiliaryClient.complete(prompt, { task: 'reflection', maxTokens: 500 });
203
+ if (!content || !content.includes('# Session Title'))
204
+ return;
205
+ const filePath = sessionPath(sessionId);
206
+ fs_1.default.writeFileSync(filePath, content, 'utf-8');
207
+ state.lastWrittenAt = Date.now();
208
+ console.log(`[SessionMemory] Written: ${filePath}`);
209
+ }
210
+ catch (e) {
211
+ console.error('[SessionMemory] Write failed:', e.message);
212
+ }
213
+ }
214
+ // ── Get last session context (inject at conversation start) ─
215
+ getLastContext(sessionId) {
216
+ // Try exact session file first
217
+ const exact = sessionPath(sessionId);
218
+ if (fs_1.default.existsSync(exact)) {
219
+ try {
220
+ return fs_1.default.readFileSync(exact, 'utf-8').slice(0, 2000);
221
+ }
222
+ catch { }
223
+ }
224
+ // Fall back to most recent session file
225
+ try {
226
+ if (!fs_1.default.existsSync(SESSIONS_DIR))
227
+ return '';
228
+ const files = fs_1.default.readdirSync(SESSIONS_DIR)
229
+ .filter(f => f.endsWith('.md'))
230
+ .map(f => ({
231
+ name: f,
232
+ mtime: fs_1.default.statSync(path_1.default.join(SESSIONS_DIR, f)).mtimeMs,
233
+ }))
234
+ .sort((a, b) => b.mtime - a.mtime);
235
+ if (files.length === 0)
236
+ return '';
237
+ const latest = path_1.default.join(SESSIONS_DIR, files[0].name);
238
+ return fs_1.default.readFileSync(latest, 'utf-8').slice(0, 2000);
239
+ }
240
+ catch {
241
+ return '';
242
+ }
243
+ }
244
+ // ── List sessions ──────────────────────────────────────────
245
+ listSessions() {
246
+ try {
247
+ if (!fs_1.default.existsSync(SESSIONS_DIR))
248
+ return [];
249
+ return fs_1.default.readdirSync(SESSIONS_DIR)
250
+ .filter(f => f.endsWith('.md'))
251
+ .sort();
252
+ }
253
+ catch {
254
+ return [];
255
+ }
256
+ }
257
+ }
258
+ // ── Singleton ──────────────────────────────────────────────────
259
+ exports.sessionMemory = new SessionMemory();
@@ -0,0 +1,91 @@
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.sessionRouter = void 0;
11
+ // core/sessionRouter.ts — Cross-channel session tracking.
12
+ // Maintains a stable sessionId per user so conversation history
13
+ // is shared regardless of whether the user is on Telegram,
14
+ // the dashboard, or any future channel.
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const path_1 = __importDefault(require("path"));
17
+ // ── SessionRouter class ────────────────────────────────────────
18
+ class SessionRouter {
19
+ constructor() {
20
+ this.sessions = new Map();
21
+ this.userToSession = new Map();
22
+ }
23
+ // ── Get or create session for a user ─────────────────────────
24
+ getSession(userId, channel) {
25
+ const existingId = this.userToSession.get(userId);
26
+ if (existingId && this.sessions.has(existingId)) {
27
+ const session = this.sessions.get(existingId);
28
+ session.lastChannel = channel;
29
+ session.lastActivity = Date.now();
30
+ if (!session.channels.includes(channel)) {
31
+ session.channels.push(channel);
32
+ console.log(`[Session] User ${userId} continued on ${channel} ` +
33
+ `(started on ${session.channels[0]})`);
34
+ }
35
+ return session;
36
+ }
37
+ // Create new session
38
+ const session = {
39
+ sessionId: `session_${Date.now()}`,
40
+ userId,
41
+ channels: [channel],
42
+ lastChannel: channel,
43
+ lastActivity: Date.now(),
44
+ messageCount: 0,
45
+ };
46
+ this.sessions.set(session.sessionId, session);
47
+ this.userToSession.set(userId, session.sessionId);
48
+ return session;
49
+ }
50
+ // ── Load persisted conversation history for a session ────────
51
+ // Reads from workspace/sessions/<sessionId>.json if it exists.
52
+ // The actual continuity comes from conversationMemory.setSession()
53
+ // receiving the stable sessionId — this is a foundation for
54
+ // future per-session file-based export / mobile sync.
55
+ getHistory(userId) {
56
+ const sessionId = this.userToSession.get(userId);
57
+ if (!sessionId)
58
+ return [];
59
+ const sessionFile = path_1.default.join(process.cwd(), 'workspace', 'sessions', `${sessionId}.json`);
60
+ if (fs_1.default.existsSync(sessionFile)) {
61
+ try {
62
+ const data = JSON.parse(fs_1.default.readFileSync(sessionFile, 'utf8'));
63
+ return data.messages || [];
64
+ }
65
+ catch { }
66
+ }
67
+ return [];
68
+ }
69
+ // ── Log a cross-channel continuation event ───────────────────
70
+ markContinuation(userId, fromChannel, toChannel) {
71
+ const session = this.getSession(userId, toChannel);
72
+ console.log(`[Session] Continuation: ${fromChannel} → ${toChannel} ` +
73
+ `(${session.messageCount} messages in history)`);
74
+ }
75
+ // ── Return channels for a given session ID ───────────────────
76
+ // Used by /api/sessions to enrich the session list response.
77
+ getSessionChannels(sessionId) {
78
+ return this.sessions.get(sessionId)?.channels ?? [];
79
+ }
80
+ // ── Expire sessions older than 24 h ──────────────────────────
81
+ cleanup() {
82
+ const cutoff = Date.now() - 24 * 60 * 60 * 1000;
83
+ for (const [id, session] of this.sessions) {
84
+ if (session.lastActivity < cutoff) {
85
+ this.sessions.delete(id);
86
+ this.userToSession.delete(session.userId);
87
+ }
88
+ }
89
+ }
90
+ }
91
+ exports.sessionRouter = new SessionRouter();