aegiscode 3.1.10 → 3.4.2

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 (209) hide show
  1. package/bin/aegis.js +165 -0
  2. package/package.json +18 -86
  3. package/LICENSE +0 -21
  4. package/README.md +0 -394
  5. package/dist/main.js +0 -2171
  6. package/gui/run-gui.cjs +0 -13
  7. package/src/agent/Agent.ts +0 -903
  8. package/src/agent/SimpleAgent.ts +0 -48
  9. package/src/agent/index.ts +0 -54
  10. package/src/agent/orchestrator/AppBuilder.ts +0 -443
  11. package/src/agent/orchestrator/CouncilAgent.ts +0 -310
  12. package/src/agent/orchestrator/DiscussionRoom.ts +0 -462
  13. package/src/agent/orchestrator/OrchestratorAgent.ts +0 -637
  14. package/src/agent/orchestrator/index.ts +0 -38
  15. package/src/agent/orchestrator/utils.ts +0 -397
  16. package/src/agent/pricing.ts +0 -115
  17. package/src/agent/router.ts +0 -74
  18. package/src/agent/routerStats.ts +0 -121
  19. package/src/agent/types.ts +0 -318
  20. package/src/auth/login.ts +0 -383
  21. package/src/cli/config.ts +0 -189
  22. package/src/cli/index.ts +0 -17
  23. package/src/cli/middleware.ts +0 -119
  24. package/src/cli/types.ts +0 -75
  25. package/src/config/ConfigManager.ts +0 -587
  26. package/src/config/index.ts +0 -7
  27. package/src/config/types.ts +0 -584
  28. package/src/context/CompactionService.ts +0 -300
  29. package/src/context/ContextManager.ts +0 -450
  30. package/src/context/FileAnalyzer.ts +0 -267
  31. package/src/context/TokenCounter.ts +0 -265
  32. package/src/context/index.ts +0 -27
  33. package/src/context/storage/CacheStore.ts +0 -176
  34. package/src/context/storage/JSONLStore.ts +0 -201
  35. package/src/context/storage/MemoryStore.ts +0 -205
  36. package/src/context/storage/PersistentStore.ts +0 -327
  37. package/src/context/storage/index.ts +0 -9
  38. package/src/context/storage/pathUtils.ts +0 -114
  39. package/src/context/test.ts +0 -309
  40. package/src/context/types.ts +0 -268
  41. package/src/hooks/HookExecutor.ts +0 -434
  42. package/src/hooks/HookManager.ts +0 -596
  43. package/src/hooks/HookService.ts +0 -269
  44. package/src/hooks/Matcher.ts +0 -157
  45. package/src/hooks/index.ts +0 -63
  46. package/src/hooks/types.ts +0 -424
  47. package/src/main.tsx +0 -596
  48. package/src/mcp/HealthMonitor.ts +0 -150
  49. package/src/mcp/McpClient.ts +0 -491
  50. package/src/mcp/McpRegistry.ts +0 -321
  51. package/src/mcp/createMcpTool.ts +0 -251
  52. package/src/mcp/index.ts +0 -15
  53. package/src/mcp/server.ts +0 -334
  54. package/src/mcp/test-server.ts +0 -88
  55. package/src/mcp/test.ts +0 -372
  56. package/src/mcp/types.ts +0 -247
  57. package/src/memory/AgentMemoryBus.ts +0 -432
  58. package/src/memory/CloudSync.ts +0 -99
  59. package/src/memory/DriveSync.ts +0 -106
  60. package/src/memory/SharedMemory.ts +0 -951
  61. package/src/memory/index.ts +0 -14
  62. package/src/memory/machineFingerprint.ts +0 -40
  63. package/src/orchestrator/SubAgentMetadata.ts +0 -136
  64. package/src/prompts/builder.ts +0 -213
  65. package/src/prompts/default.ts +0 -144
  66. package/src/prompts/index.ts +0 -16
  67. package/src/prompts/plan.ts +0 -64
  68. package/src/prompts/test.ts +0 -78
  69. package/src/services/AnthropicChatService.ts +0 -341
  70. package/src/services/ChatService.ts +0 -347
  71. package/src/services/ClaudeCliChatService.ts +0 -256
  72. package/src/services/CloudSync.ts +0 -168
  73. package/src/services/CostLedger.ts +0 -211
  74. package/src/services/Heartbeat.ts +0 -135
  75. package/src/services/LearningCollector.ts +0 -291
  76. package/src/services/OllamaInstaller.ts +0 -342
  77. package/src/services/VersionChecker.ts +0 -445
  78. package/src/services/index.ts +0 -57
  79. package/src/services/streaming/OpenAIEventAdapter.ts +0 -126
  80. package/src/services/streaming/RenderingProfile.ts +0 -90
  81. package/src/services/streaming/StreamEventParser.ts +0 -181
  82. package/src/services/streaming/ThrottledRenderer.ts +0 -139
  83. package/src/services/streaming/TranscriptBuffer.ts +0 -574
  84. package/src/services/streaming/eventStatusMap.ts +0 -52
  85. package/src/services/streaming/index.ts +0 -46
  86. package/src/services/streaming/renderFormatting.ts +0 -79
  87. package/src/services/streaming/types.ts +0 -234
  88. package/src/skills/SkillLoader.ts +0 -126
  89. package/src/skills/SkillRegistry.ts +0 -366
  90. package/src/skills/index.ts +0 -48
  91. package/src/skills/types.ts +0 -146
  92. package/src/slash-commands/billing.ts +0 -70
  93. package/src/slash-commands/build.ts +0 -413
  94. package/src/slash-commands/builtinCommands.ts +0 -2733
  95. package/src/slash-commands/clone.ts +0 -242
  96. package/src/slash-commands/council.ts +0 -125
  97. package/src/slash-commands/custom/CustomCommandExecutor.ts +0 -143
  98. package/src/slash-commands/custom/CustomCommandLoader.ts +0 -251
  99. package/src/slash-commands/custom/CustomCommandRegistry.ts +0 -144
  100. package/src/slash-commands/custom/index.ts +0 -7
  101. package/src/slash-commands/debate.ts +0 -254
  102. package/src/slash-commands/gmail.ts +0 -105
  103. package/src/slash-commands/index.ts +0 -388
  104. package/src/slash-commands/mcpCommand.ts +0 -205
  105. package/src/slash-commands/types.ts +0 -201
  106. package/src/store/index.ts +0 -76
  107. package/src/store/selectors.ts +0 -246
  108. package/src/store/slices/appSlice.ts +0 -205
  109. package/src/store/slices/commandSlice.ts +0 -115
  110. package/src/store/slices/configSlice.ts +0 -46
  111. package/src/store/slices/focusSlice.ts +0 -64
  112. package/src/store/slices/index.ts +0 -9
  113. package/src/store/slices/sessionSlice.ts +0 -424
  114. package/src/store/streaming-buffer.ts +0 -425
  115. package/src/store/test.ts +0 -296
  116. package/src/store/types.ts +0 -274
  117. package/src/store/vanilla.ts +0 -186
  118. package/src/tools/builtin/bash.ts +0 -236
  119. package/src/tools/builtin/council.ts +0 -105
  120. package/src/tools/builtin/edit.ts +0 -213
  121. package/src/tools/builtin/glob.ts +0 -136
  122. package/src/tools/builtin/grep.ts +0 -263
  123. package/src/tools/builtin/index.ts +0 -61
  124. package/src/tools/builtin/memory.ts +0 -66
  125. package/src/tools/builtin/read.ts +0 -168
  126. package/src/tools/builtin/skill.ts +0 -97
  127. package/src/tools/builtin/snapshot.ts +0 -40
  128. package/src/tools/builtin/task.ts +0 -106
  129. package/src/tools/builtin/write.ts +0 -134
  130. package/src/tools/createTool.ts +0 -221
  131. package/src/tools/execution/ExecutionPipeline.ts +0 -263
  132. package/src/tools/execution/index.ts +0 -40
  133. package/src/tools/execution/stages/CacheStage.ts +0 -131
  134. package/src/tools/execution/stages/ConfirmationStage.ts +0 -203
  135. package/src/tools/execution/stages/DiscoveryStage.ts +0 -46
  136. package/src/tools/execution/stages/ExecutionStage.ts +0 -48
  137. package/src/tools/execution/stages/FormattingStage.ts +0 -44
  138. package/src/tools/execution/stages/HookStage.ts +0 -72
  139. package/src/tools/execution/stages/PermissionStage.ts +0 -287
  140. package/src/tools/execution/stages/PostHookStage.ts +0 -71
  141. package/src/tools/execution/stages/index.ts +0 -12
  142. package/src/tools/execution/test.ts +0 -266
  143. package/src/tools/execution/types.ts +0 -273
  144. package/src/tools/index.ts +0 -81
  145. package/src/tools/registry.ts +0 -304
  146. package/src/tools/schemas.ts +0 -109
  147. package/src/tools/test.ts +0 -220
  148. package/src/tools/types.ts +0 -175
  149. package/src/tools/validation/PermissionChecker.ts +0 -242
  150. package/src/tools/validation/SensitiveFileDetector.ts +0 -210
  151. package/src/tools/validation/index.ts +0 -11
  152. package/src/ui/App.tsx +0 -166
  153. package/src/ui/components/AegisInterface.tsx +0 -484
  154. package/src/ui/components/common/ChatSearch.tsx +0 -150
  155. package/src/ui/components/common/ErrorBoundary.tsx +0 -82
  156. package/src/ui/components/common/ExitMessage.tsx +0 -120
  157. package/src/ui/components/common/LoadingIndicator.tsx +0 -49
  158. package/src/ui/components/common/index.ts +0 -6
  159. package/src/ui/components/dialog/ConfirmationPrompt.tsx +0 -208
  160. package/src/ui/components/dialog/InteractiveSelector.tsx +0 -149
  161. package/src/ui/components/dialog/SetupWizard.tsx +0 -297
  162. package/src/ui/components/dialog/UpdatePrompt.tsx +0 -155
  163. package/src/ui/components/dialog/index.ts +0 -8
  164. package/src/ui/components/index.ts +0 -28
  165. package/src/ui/components/input/CommandSuggestions.tsx +0 -139
  166. package/src/ui/components/input/CustomTextInput.tsx +0 -220
  167. package/src/ui/components/input/InputArea.tsx +0 -361
  168. package/src/ui/components/input/PromptSuggestions.tsx +0 -66
  169. package/src/ui/components/input/index.ts +0 -6
  170. package/src/ui/components/layout/ChatStatusBar.tsx +0 -90
  171. package/src/ui/components/layout/ContextBar.tsx +0 -79
  172. package/src/ui/components/layout/MessageArea.tsx +0 -96
  173. package/src/ui/components/layout/MessageList.tsx +0 -647
  174. package/src/ui/components/layout/MessageSeparator.tsx +0 -26
  175. package/src/ui/components/layout/WelcomeMessage.tsx +0 -93
  176. package/src/ui/components/layout/index.ts +0 -7
  177. package/src/ui/components/markdown/CodeHighlighter.tsx +0 -292
  178. package/src/ui/components/markdown/MessageRenderer.tsx +0 -1211
  179. package/src/ui/components/markdown/index.ts +0 -8
  180. package/src/ui/components/markdown/parser.ts +0 -336
  181. package/src/ui/components/markdown/types.ts +0 -66
  182. package/src/ui/focus/FocusManager.ts +0 -137
  183. package/src/ui/focus/index.ts +0 -13
  184. package/src/ui/focus/types.ts +0 -54
  185. package/src/ui/focus/useFocus.ts +0 -75
  186. package/src/ui/hooks/index.ts +0 -11
  187. package/src/ui/hooks/useAgent.ts +0 -284
  188. package/src/ui/hooks/useCommandHistory.ts +0 -87
  189. package/src/ui/hooks/useCommandProcessor.ts +0 -443
  190. package/src/ui/hooks/useConfirmation.ts +0 -99
  191. package/src/ui/hooks/useCtrlCHandler.ts +0 -100
  192. package/src/ui/hooks/useInputBuffer.ts +0 -122
  193. package/src/ui/hooks/useTerminalSize.ts +0 -68
  194. package/src/ui/hooks/useTerminalWidth.ts +0 -5
  195. package/src/ui/hooks/useWindowedList.ts +0 -118
  196. package/src/ui/render-debugger.ts +0 -621
  197. package/src/ui/test.ts +0 -189
  198. package/src/ui/themes/ThemeManager.ts +0 -332
  199. package/src/ui/themes/aegisTheme.ts +0 -87
  200. package/src/ui/themes/darkTheme.ts +0 -87
  201. package/src/ui/themes/defaultTheme.ts +0 -85
  202. package/src/ui/themes/index.ts +0 -10
  203. package/src/ui/themes/lightTheme.ts +0 -89
  204. package/src/ui/themes/popularThemes.ts +0 -187
  205. package/src/ui/themes/types.ts +0 -130
  206. package/src/utils/clipboard.ts +0 -48
  207. package/src/utils/debug.ts +0 -43
  208. package/src/utils/environment.ts +0 -68
  209. package/src/utils/index.ts +0 -10
@@ -1,432 +0,0 @@
1
- /**
2
- * AgentMemoryBus — Shared memory bus for multi-agent communication
3
- *
4
- * Purpose:
5
- * Allows multiple agents (Orchestrator sub-agents, Council members)
6
- * to read/write shared contextual memory in real-time.
7
- *
8
- * Design:
9
- * - In-memory fast cache for active session + SQLite persistence
10
- * - Channels: each agent publishes to typed channels
11
- * - Sub-agents automatically get relevant context from prior agents
12
- * - Supports TTL-based expiration, importance scoring, cross-referencing
13
- *
14
- * Integration with existing SharedMemory:
15
- * AgentMemoryBus uses SharedMemory as its persistent backing store,
16
- * adding a lightweight pub/sub layer on top for multi-agent coordination.
17
- */
18
-
19
- import { sharedMemory, type MemoryEntry } from './SharedMemory.js';
20
- import { v4 as uuid } from 'uuid';
21
- import { agentDebug } from '../utils/debug.js';
22
-
23
- // ── Types ────────────────────────────────────────────────────────────────────
24
-
25
- export type AgentChannel =
26
- | 'decision' // Agent decisions / conclusions
27
- | 'fact' // Facts discovered during work
28
- | 'context' // Contextual information (files, code, env)
29
- | 'intermediate' // Intermediate results
30
- | 'error' // Errors encountered
31
- | 'question' // Questions to other agents
32
- | 'suggestion'; // Suggestions for next steps
33
-
34
- export interface AgentMemoryMessage {
35
- id: string;
36
- channel: AgentChannel;
37
- sourceAgent: string;
38
- targetAgent?: string; // If specified, only this agent should consume
39
- sessionId: string;
40
- content: string;
41
- timestamp: string;
42
- importance: number;
43
- references?: string[]; // IDs of related messages
44
- ttl?: number; // Seconds until auto-expire (0 = forever)
45
- tags?: string[];
46
- metadata?: Record<string, unknown>;
47
- }
48
-
49
- export interface AgentMemoryQuery {
50
- channels?: AgentChannel[];
51
- sourceAgent?: string;
52
- targetAgent?: string;
53
- sessionId?: string;
54
- query?: string; // Free-text search
55
- limit?: number;
56
- minImportance?: number;
57
- maxAgeSeconds?: number;
58
- }
59
-
60
- // ── In-memory fast cache ─────────────────────────────────────────────────────
61
-
62
- interface CacheEntry {
63
- msg: AgentMemoryMessage;
64
- expiresAt: number; // 0 = never
65
- }
66
-
67
- class MemoryCache {
68
- private cache = new Map<string, CacheEntry>();
69
- private byChannel = new Map<AgentChannel, Set<string>>();
70
- private bySession = new Map<string, Set<string>>();
71
- private bySource = new Map<string, Set<string>>();
72
-
73
- put(msg: AgentMemoryMessage): void {
74
- const expiresAt = msg.ttl && msg.ttl > 0 ? Date.now() + msg.ttl * 1000 : 0;
75
- this.cache.set(msg.id, { msg, expiresAt });
76
-
77
- if (!this.byChannel.has(msg.channel)) this.byChannel.set(msg.channel, new Set());
78
- this.byChannel.get(msg.channel)!.add(msg.id);
79
-
80
- if (!this.bySession.has(msg.sessionId)) this.bySession.set(msg.sessionId, new Set());
81
- this.bySession.get(msg.sessionId)!.add(msg.id);
82
-
83
- if (!this.bySource.has(msg.sourceAgent)) this.bySource.set(msg.sourceAgent, new Set());
84
- this.bySource.get(msg.sourceAgent)!.add(msg.id);
85
- }
86
-
87
- get(id: string): AgentMemoryMessage | null {
88
- const entry = this.cache.get(id);
89
- if (!entry) return null;
90
- if (entry.expiresAt > 0 && Date.now() > entry.expiresAt) {
91
- this.cache.delete(id);
92
- return null;
93
- }
94
- return entry.msg;
95
- }
96
-
97
- query(q: AgentMemoryQuery): AgentMemoryMessage[] {
98
- this.evictExpired();
99
-
100
- let candidateIds = new Set(this.cache.keys());
101
-
102
- if (q.channels && q.channels.length > 0) {
103
- const channelIds = new Set<string>();
104
- for (const ch of q.channels) {
105
- const ids = this.byChannel.get(ch);
106
- if (ids) for (const id of ids) channelIds.add(id);
107
- }
108
- candidateIds = intersect(candidateIds, channelIds);
109
- }
110
-
111
- if (q.sessionId) {
112
- const sessionIds = this.bySession.get(q.sessionId);
113
- candidateIds = sessionIds
114
- ? intersect(candidateIds, sessionIds)
115
- : new Set();
116
- }
117
-
118
- if (q.sourceAgent) {
119
- const sourceIds = this.bySource.get(q.sourceAgent);
120
- candidateIds = sourceIds
121
- ? intersect(candidateIds, sourceIds)
122
- : new Set();
123
- }
124
-
125
- if (q.targetAgent) {
126
- const filtered = new Set<string>();
127
- for (const id of candidateIds) {
128
- const msg = this.cache.get(id)?.msg;
129
- if (msg && (!msg.targetAgent || msg.targetAgent === q.targetAgent)) {
130
- filtered.add(id);
131
- }
132
- }
133
- candidateIds = filtered;
134
- }
135
-
136
- let results = Array.from(candidateIds)
137
- .map(id => this.cache.get(id)!.msg)
138
- .filter(msg => {
139
- if (q.minImportance !== undefined && msg.importance < q.minImportance) return false;
140
- if (q.maxAgeSeconds !== undefined) {
141
- const age = (Date.now() - new Date(msg.timestamp).getTime()) / 1000;
142
- if (age > q.maxAgeSeconds) return false;
143
- }
144
- return true;
145
- });
146
-
147
- // Free-text filter
148
- if (q.query) {
149
- const lower = q.query.toLowerCase();
150
- results = results.filter(m =>
151
- m.content.toLowerCase().includes(lower) ||
152
- (m.tags || []).some(t => t.toLowerCase().includes(lower))
153
- );
154
- }
155
-
156
- // Sort by importance desc, then timestamp desc
157
- results.sort((a, b) => {
158
- const imp = b.importance - a.importance;
159
- if (imp !== 0) return imp;
160
- return b.timestamp.localeCompare(a.timestamp);
161
- });
162
-
163
- return results.slice(0, q.limit || 20);
164
- }
165
-
166
- clearSession(sessionId: string): void {
167
- const ids = this.bySession.get(sessionId);
168
- if (!ids) return;
169
- for (const id of ids) {
170
- const msg = this.cache.get(id)?.msg;
171
- if (msg) {
172
- this.byChannel.get(msg.channel)?.delete(id);
173
- this.bySource.get(msg.sourceAgent)?.delete(id);
174
- this.cache.delete(id);
175
- }
176
- }
177
- this.bySession.delete(sessionId);
178
- }
179
-
180
- size(): number {
181
- this.evictExpired();
182
- return this.cache.size;
183
- }
184
-
185
- private evictExpired(): void {
186
- const now = Date.now();
187
- for (const [id, entry] of this.cache) {
188
- if (entry.expiresAt > 0 && now > entry.expiresAt) {
189
- const msg = entry.msg;
190
- this.byChannel.get(msg.channel)?.delete(id);
191
- this.bySession.get(msg.sessionId)?.delete(id);
192
- this.bySource.get(msg.sourceAgent)?.delete(id);
193
- this.cache.delete(id);
194
- }
195
- }
196
- }
197
- }
198
-
199
- function intersect(a: Set<string>, b: Set<string>): Set<string> {
200
- const result = new Set<string>();
201
- for (const item of a) {
202
- if (b.has(item)) result.add(item);
203
- }
204
- return result;
205
- }
206
-
207
- // ── AgentMemoryBus ───────────────────────────────────────────────────────────
208
-
209
- export class AgentMemoryBus {
210
- private cache: MemoryCache;
211
- private subscribers: Map<AgentChannel, Set<(msg: AgentMemoryMessage) => void>> = new Map();
212
- private globalSubscribers: Set<(msg: AgentMemoryMessage) => void> = new Set();
213
- private persistenceEnabled: boolean;
214
-
215
- constructor(persistToSharedMemory = true) {
216
- this.cache = new MemoryCache();
217
- this.persistenceEnabled = persistToSharedMemory;
218
- }
219
-
220
- /**
221
- * Publish a message to the shared memory bus.
222
- * Stored in fast cache + optionally persisted to SharedMemory SQLite.
223
- */
224
- async publish(msg: Omit<AgentMemoryMessage, 'id' | 'timestamp'>): Promise<AgentMemoryMessage> {
225
- const full: AgentMemoryMessage = {
226
- ...msg,
227
- id: uuid(),
228
- timestamp: new Date().toISOString(),
229
- };
230
-
231
- // 1. Fast in-memory cache
232
- this.cache.put(full);
233
-
234
- // 2. Notify subscribers
235
- this.notifySubscribers(full);
236
-
237
- // 3. Persist to SharedMemory SQLite (if enabled)
238
- if (this.persistenceEnabled) {
239
- const channelTag = `agent:${msg.channel}`;
240
- const sourceTag = `agent:${msg.sourceAgent}`;
241
- const tags = [...(msg.tags || []), channelTag, sourceTag];
242
- if (msg.targetAgent) tags.push(`target:${msg.targetAgent}`);
243
-
244
- const importance = msg.importance;
245
-
246
- // Store as a memory entry so it appears in semantic search
247
- sharedMemory.add(
248
- `[AgentBus:${msg.channel}] [${msg.sourceAgent}]${msg.targetAgent ? ` → ${msg.targetAgent}` : ''}: ${msg.content}`,
249
- `agent-bus-${msg.channel}`,
250
- msg.sessionId,
251
- tags,
252
- 'assistant',
253
- false, // defer commit for batching
254
- ).catch(() => {});
255
-
256
- // High-importance items get stored with extra metadata
257
- if (importance >= 0.7) {
258
- sharedMemory.add(
259
- `[AgentBus:${msg.channel}] [DECISION] ${msg.sourceAgent}: ${msg.content}`,
260
- 'agent-bus-decision',
261
- msg.sessionId,
262
- [...tags, 'high-importance', 'decision'],
263
- 'assistant',
264
- true,
265
- ).catch(() => {});
266
- }
267
- }
268
-
269
- return full;
270
- }
271
-
272
- /**
273
- * Query messages from the shared memory bus (cache + persistent)
274
- */
275
- async query(q: AgentMemoryQuery): Promise<AgentMemoryMessage[]> {
276
- // 1. Get from fast cache
277
- const cached = this.cache.query(q);
278
-
279
- // 2. If we need more, search SharedMemory
280
- if (cached.length < (q.limit || 20) && this.persistenceEnabled) {
281
- const searchQuery = q.query || q.channels?.join(' ') || '';
282
- const persisted = await sharedMemory.search(
283
- `AgentBus ${searchQuery}`,
284
- (q.limit || 20) - cached.length
285
- );
286
-
287
- // Convert MemoryEntry → AgentMemoryMessage (best-effort)
288
- const fromPersisted: AgentMemoryMessage[] = persisted
289
- .filter(e => e.content.startsWith('[AgentBus'))
290
- .map(e => this.entryToMessage(e))
291
- .filter((m): m is AgentMemoryMessage => m !== null);
292
-
293
- // Merge, deduplicate by ID
294
- const seen = new Set(cached.map(m => m.id));
295
- for (const m of fromPersisted) {
296
- if (!seen.has(m.id)) {
297
- cached.push(m);
298
- seen.add(m.id);
299
- }
300
- }
301
- }
302
-
303
- return cached;
304
- }
305
-
306
- /**
307
- * Subscribe to messages on a specific channel
308
- */
309
- subscribe(channel: AgentChannel, callback: (msg: AgentMemoryMessage) => void): () => void {
310
- if (!this.subscribers.has(channel)) {
311
- this.subscribers.set(channel, new Set());
312
- }
313
- this.subscribers.get(channel)!.add(callback);
314
- return () => this.subscribers.get(channel)?.delete(callback);
315
- }
316
-
317
- /**
318
- * Subscribe to all channels
319
- */
320
- subscribeAll(callback: (msg: AgentMemoryMessage) => void): () => void {
321
- this.globalSubscribers.add(callback);
322
- return () => this.globalSubscribers.delete(callback);
323
- }
324
-
325
- /**
326
- * Get context for an agent: relevant messages from other agents in the same session
327
- */
328
- async getContextForAgent(
329
- agentName: string,
330
- sessionId: string,
331
- limit = 10,
332
- maxAgeSeconds = 300, // last 5 minutes by default
333
- ): Promise<string> {
334
- const relevant = await this.query({
335
- sessionId,
336
- limit,
337
- maxAgeSeconds,
338
- });
339
-
340
- // Filter out messages from the requesting agent
341
- const others = relevant.filter(m => m.sourceAgent !== agentName);
342
-
343
- if (others.length === 0) return '';
344
-
345
- const lines: string[] = [
346
- '--- AGENT MEMORY (shared context from other agents) ---',
347
- ];
348
-
349
- for (const m of others) {
350
- const target = m.targetAgent ? ` → ${m.targetAgent}` : '';
351
- lines.push(
352
- `[${m.channel}] ${m.sourceAgent}${target} (${m.timestamp.slice(11, 19)}): ${m.content.slice(0, 300)}`
353
- );
354
- if (m.importance >= 0.8) lines.push(` ⭐ Important: ${m.content.slice(0, 200)}`);
355
- }
356
-
357
- lines.push('--- END AGENT MEMORY ---');
358
- return lines.join('\n');
359
- }
360
-
361
- /**
362
- * Clear all messages for a session
363
- */
364
- clearSession(sessionId: string): void {
365
- this.cache.clearSession(sessionId);
366
- }
367
-
368
- /**
369
- * Get stats
370
- */
371
- stats(): { cacheSize: number; subscribers: number; channels: number } {
372
- let subCount = this.globalSubscribers.size;
373
- for (const subs of this.subscribers.values()) {
374
- subCount += subs.size;
375
- }
376
- return {
377
- cacheSize: this.cache.size(),
378
- subscribers: subCount,
379
- channels: this.subscribers.size,
380
- };
381
- }
382
-
383
- // ── Private ──────────────────────────────────────────────────────────────
384
-
385
- private notifySubscribers(msg: AgentMemoryMessage): void {
386
- // Channel-specific
387
- const channelSubs = this.subscribers.get(msg.channel);
388
- if (channelSubs) {
389
- for (const cb of channelSubs) {
390
- try { cb(msg); } catch { /* subscriber error */ }
391
- }
392
- }
393
- // Global
394
- for (const cb of this.globalSubscribers) {
395
- try { cb(msg); } catch { /* subscriber error */ }
396
- }
397
- }
398
-
399
- private entryToMessage(entry: MemoryEntry): AgentMemoryMessage | null {
400
- try {
401
- const content = entry.content;
402
- // Two stored formats:
403
- // normal: [AgentBus:channel] [sourceAgent] → targetAgent?: body
404
- // decision: [AgentBus:channel] [DECISION] sourceAgent: body
405
- const channelMatch = content.match(/^\[AgentBus:(\w+)\]/);
406
- const decisionMatch = content.match(/\[DECISION\] (\w+):/);
407
- const sourceMatch = decisionMatch ?? content.match(/\] \[(\w+)\]/);
408
- const targetMatch = content.match(/\] → (\w+):/);
409
- const contentStart = content.indexOf(': ', content.lastIndexOf(']'));
410
-
411
- if (!channelMatch || !sourceMatch) return null;
412
-
413
- return {
414
- id: entry.id,
415
- channel: channelMatch[1] as AgentChannel,
416
- sourceAgent: sourceMatch[1],
417
- targetAgent: targetMatch ? targetMatch[1] : undefined,
418
- sessionId: entry.session,
419
- content: contentStart > 0 ? content.slice(contentStart + 2) : content,
420
- timestamp: entry.timestamp,
421
- importance: entry.importance || 0.5,
422
- tags: entry.tags,
423
- };
424
- } catch {
425
- return null;
426
- }
427
- }
428
- }
429
-
430
- // ── Singleton ────────────────────────────────────────────────────────────────
431
-
432
- export const agentMemoryBus = new AgentMemoryBus(true);
@@ -1,99 +0,0 @@
1
- /**
2
- * AEGIS Memory Cloud Sync
3
- *
4
- * Pushes/pulls SharedMemory entries through aegiscloud.org so memory is
5
- * shared across devices (CLI + mobile) for subscribed users. Best-effort:
6
- * failures are swallowed, matching DriveSync's "silent fail — optional"
7
- * pattern. Never blocks the main session — callers fire-and-forget.
8
- */
9
- import type { MemoryEntry } from './SharedMemory.js';
10
-
11
- const MEMORY_API_BASE = process.env.AEGIS_MEMORY_API_BASE || 'https://aegiscloud.org/api/memory';
12
-
13
- // Embeddings are 384 floats — as JSON that's ~7KB per entry. The server only
14
- // ever does keyword (LIKE) search/dashboard listing on memory_entries, never
15
- // vector similarity, so transmitting them bloats every push for zero server-side
16
- // benefit. A bulk upload of a few thousand entries turned tens of MB of pure
17
- // embedding JSON across many sequential requests, which is what was actually
18
- // timing out — not a network problem. Dropping it: a device that pulls this
19
- // entry down just lacks a precomputed vector locally (falls back to keyword
20
- // search for it, same as any entry whose embedder failed at add()-time).
21
- function stripEmbedding(entries: MemoryEntry[]): Omit<MemoryEntry, 'embedding'>[] {
22
- return entries.map(({ embedding, ...rest }) => rest);
23
- }
24
-
25
- export async function pushEntries(entries: MemoryEntry[], apiKey: string): Promise<void> {
26
- if (entries.length === 0) return;
27
- try {
28
- await fetch(`${MEMORY_API_BASE}/save`, {
29
- method: 'POST',
30
- headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
31
- body: JSON.stringify({ entries: stripEmbedding(entries) }),
32
- });
33
- } catch {
34
- // silent fail — sync is optional, local memory already has the entry
35
- }
36
- }
37
-
38
- /** Like pushEntries, but reports how many entries the server actually saved (for bulk uploads). */
39
- export async function pushBatch(entries: MemoryEntry[], apiKey: string): Promise<number> {
40
- if (entries.length === 0) return 0;
41
- try {
42
- // A bulk upload can be many sequential batches — one slow/hung request
43
- // without its own timeout would stall the whole pushAll() loop indefinitely,
44
- // regardless of any timeout the caller process is given from outside.
45
- const controller = new AbortController();
46
- const timer = setTimeout(() => controller.abort(), 20_000);
47
- let res: Response;
48
- try {
49
- res = await fetch(`${MEMORY_API_BASE}/save`, {
50
- method: 'POST',
51
- headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
52
- body: JSON.stringify({ entries: stripEmbedding(entries) }),
53
- signal: controller.signal,
54
- });
55
- } finally {
56
- clearTimeout(timer);
57
- }
58
- if (!res.ok) return 0;
59
- const data = await res.json() as { saved?: number };
60
- return data.saved ?? 0;
61
- } catch {
62
- return 0;
63
- }
64
- }
65
-
66
- export async function pullSince(since: string | null, apiKey: string): Promise<MemoryEntry[]> {
67
- try {
68
- const res = await fetch(`${MEMORY_API_BASE}/pull`, {
69
- method: 'POST',
70
- headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
71
- body: JSON.stringify(since ? { since } : {}),
72
- });
73
- if (!res.ok) return [];
74
- const data = await res.json() as { entries?: MemoryEntry[] };
75
- return data.entries ?? [];
76
- } catch {
77
- return [];
78
- }
79
- }
80
-
81
- /**
82
- * Keyword search against the caller's synced entries (catches entries written on
83
- * another device since the last `pullSince`). Caller is responsible for merging
84
- * with local results — this never throws, just returns [] on any failure.
85
- */
86
- export async function searchCloud(query: string, limit: number, apiKey: string): Promise<MemoryEntry[]> {
87
- try {
88
- const res = await fetch(`${MEMORY_API_BASE}/search`, {
89
- method: 'POST',
90
- headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` },
91
- body: JSON.stringify({ query, limit }),
92
- });
93
- if (!res.ok) return [];
94
- const data = await res.json() as { entries?: MemoryEntry[] };
95
- return data.entries ?? [];
96
- } catch {
97
- return [];
98
- }
99
- }
@@ -1,106 +0,0 @@
1
- /**
2
- * AEGIS Drive Sync
3
- * Uploads conversation sessions to user's Google Drive
4
- */
5
- import * as fs from 'fs';
6
- import * as path from 'path';
7
- import * as os from 'os';
8
- import * as https from 'https';
9
-
10
- const CONFIG_PATH = path.join(os.homedir(), '.aegiscode', 'config.json');
11
- const SESSIONS_DIR = path.join(os.homedir(), '.aegis', 'projects');
12
-
13
- function getConfig() {
14
- try { return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); }
15
- catch { return {}; }
16
- }
17
-
18
- async function getDriveToken(): Promise<string | null> {
19
- const cfg = getConfig();
20
- const apiKey = cfg?.aegiscloud?.api_key || process.env.AEGISCLOUD_API_KEY;
21
- if (!apiKey) return null;
22
- return new Promise((resolve) => {
23
- const req = https.request({
24
- hostname: 'aegiscloud.org',
25
- path: '/api/drive/token',
26
- method: 'GET',
27
- headers: { 'X-API-Key': apiKey }
28
- }, res => {
29
- let data = '';
30
- res.on('data', c => data += c);
31
- res.on('end', () => {
32
- try {
33
- const d = JSON.parse(data);
34
- resolve(d.connected ? d.access_token : null);
35
- } catch { resolve(null); }
36
- });
37
- });
38
- req.on('error', () => resolve(null));
39
- req.end();
40
- });
41
- }
42
-
43
-
44
- async function uploadToDrive(token: string, filename: string, content: string): Promise<void> {
45
- return new Promise((resolve, reject) => {
46
- const boundary = '-------aegis_boundary';
47
- const metadata = JSON.stringify({ name: filename, parents: ['root'] });
48
- const body = [
49
- `--${boundary}`,
50
- 'Content-Type: application/json; charset=UTF-8',
51
- '',
52
- metadata,
53
- `--${boundary}`,
54
- 'Content-Type: text/plain',
55
- '',
56
- content,
57
- `--${boundary}--`,
58
- ].join('\r\n');
59
-
60
- const req = https.request({
61
- hostname: 'www.googleapis.com',
62
- path: '/upload/drive/v3/files?uploadType=multipart',
63
- method: 'POST',
64
- headers: {
65
- 'Authorization': `Bearer ${token}`,
66
- 'Content-Type': `multipart/related; boundary=${boundary}`,
67
- 'Content-Length': Buffer.byteLength(body),
68
- }
69
- }, res => {
70
- let data = '';
71
- res.on('data', c => data += c);
72
- res.on('end', () => {
73
- if (res.statusCode === 200 || res.statusCode === 201) resolve();
74
- else reject(new Error(`Drive upload failed: ${res.statusCode}`));
75
- });
76
- });
77
- req.on('error', reject);
78
- req.write(body);
79
- req.end();
80
- });
81
- }
82
-
83
- export async function syncSessionToDrive(sessionId: string): Promise<void> {
84
- const token = await getDriveToken();
85
- if (!token) return;
86
-
87
- // Find session file
88
- const projects = fs.readdirSync(SESSIONS_DIR).filter(d =>
89
- fs.statSync(path.join(SESSIONS_DIR, d)).isDirectory()
90
- );
91
-
92
- for (const project of projects) {
93
- const sessionFile = path.join(SESSIONS_DIR, project, `${sessionId}.jsonl`);
94
- if (fs.existsSync(sessionFile)) {
95
- const content = fs.readFileSync(sessionFile, 'utf8');
96
- const filename = `AEGIS/${project}/${sessionId}.jsonl`;
97
- try {
98
- await uploadToDrive(token, filename, content);
99
- console.log(`\x1b[38;2;68;64;90m[Drive] Synced ${sessionId}\x1b[0m`);
100
- } catch(e) {
101
- // Silent fail — Drive sync is optional
102
- }
103
- return;
104
- }
105
- }
106
- }