myceliumail 1.0.9 β†’ 1.0.13

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 (96) hide show
  1. package/.mappersan/outbox.json +15 -0
  2. package/.spidersan/registry.json +39 -0
  3. package/CHANGELOG.md +29 -0
  4. package/CLAUDE.md +29 -0
  5. package/README.md +23 -2
  6. package/dist/bin/myceliumail.js +17 -1
  7. package/dist/bin/myceliumail.js.map +1 -1
  8. package/dist/commands/close.d.ts +9 -0
  9. package/dist/commands/close.d.ts.map +1 -0
  10. package/dist/commands/close.js +153 -0
  11. package/dist/commands/close.js.map +1 -0
  12. package/dist/commands/collab.d.ts +8 -0
  13. package/dist/commands/collab.d.ts.map +1 -0
  14. package/dist/commands/collab.js +112 -0
  15. package/dist/commands/collab.js.map +1 -0
  16. package/dist/commands/inbox.d.ts.map +1 -1
  17. package/dist/commands/inbox.js +105 -26
  18. package/dist/commands/inbox.js.map +1 -1
  19. package/dist/commands/tags.d.ts +6 -0
  20. package/dist/commands/tags.d.ts.map +1 -0
  21. package/dist/commands/tags.js +90 -0
  22. package/dist/commands/tags.js.map +1 -0
  23. package/dist/commands/wake.d.ts +9 -0
  24. package/dist/commands/wake.d.ts.map +1 -0
  25. package/dist/commands/wake.js +198 -0
  26. package/dist/commands/wake.js.map +1 -0
  27. package/dist/dashboard/public/app.js +117 -0
  28. package/dist/dashboard/public/index.html +63 -5
  29. package/dist/dashboard/routes.d.ts.map +1 -1
  30. package/dist/dashboard/routes.js +31 -2
  31. package/dist/dashboard/routes.js.map +1 -1
  32. package/dist/lib/update-check.d.ts.map +1 -1
  33. package/dist/lib/update-check.js +6 -4
  34. package/dist/lib/update-check.js.map +1 -1
  35. package/dist/lib/watson-digest.d.ts +40 -0
  36. package/dist/lib/watson-digest.d.ts.map +1 -0
  37. package/dist/lib/watson-digest.js +164 -0
  38. package/dist/lib/watson-digest.js.map +1 -0
  39. package/dist/storage/supabase.d.ts +4 -0
  40. package/dist/storage/supabase.d.ts.map +1 -1
  41. package/dist/storage/supabase.js +57 -0
  42. package/dist/storage/supabase.js.map +1 -1
  43. package/docs/COLLAB_mappersan_mycmail_setup.md +115 -0
  44. package/docs/COLLAB_wake_close_commands.md +518 -0
  45. package/docs/CROSS_TOOL_INTEGRATION_PLAN.md +246 -0
  46. package/docs/JSON_SCHEMA_WAKE_CLOSE.md +246 -0
  47. package/docs/MYCMAIL_QUICKSTART.md +103 -0
  48. package/docs/WAKE_AGENTS_SHARED_DOC.md +1215 -0
  49. package/mcp-server/README.md +75 -69
  50. package/mcp-server/package-lock.json +2 -2
  51. package/mcp-server/package.json +5 -1
  52. package/mcp-server/postinstall.js +14 -0
  53. package/mcp-server/src/server.ts +39 -0
  54. package/mobile-app/README.md +36 -0
  55. package/mobile-app/app/compose/page.tsx +140 -0
  56. package/mobile-app/app/favicon.ico +0 -0
  57. package/mobile-app/app/globals.css +26 -0
  58. package/mobile-app/app/layout.tsx +42 -0
  59. package/mobile-app/app/message/[id]/page.tsx +126 -0
  60. package/mobile-app/app/page.tsx +131 -0
  61. package/mobile-app/components/MessageCard.tsx +60 -0
  62. package/mobile-app/eslint.config.mjs +18 -0
  63. package/mobile-app/lib/supabase.ts +87 -0
  64. package/mobile-app/next.config.ts +7 -0
  65. package/mobile-app/package-lock.json +6674 -0
  66. package/mobile-app/package.json +27 -0
  67. package/mobile-app/postcss.config.mjs +7 -0
  68. package/mobile-app/public/file.svg +1 -0
  69. package/mobile-app/public/globe.svg +1 -0
  70. package/mobile-app/public/next.svg +1 -0
  71. package/mobile-app/public/vercel.svg +1 -0
  72. package/mobile-app/public/window.svg +1 -0
  73. package/mobile-app/tsconfig.json +34 -0
  74. package/package.json +2 -1
  75. package/postinstall.js +14 -0
  76. package/src/bin/myceliumail.ts +19 -1
  77. package/src/commands/close.ts +172 -0
  78. package/src/commands/collab.ts +125 -0
  79. package/src/commands/inbox.ts +120 -29
  80. package/src/commands/tags.ts +102 -0
  81. package/src/commands/wake.ts +228 -0
  82. package/src/dashboard/public/app.js +117 -0
  83. package/src/dashboard/public/index.html +63 -5
  84. package/src/dashboard/routes.ts +31 -2
  85. package/src/lib/update-check.ts +7 -4
  86. package/src/lib/watson-digest.ts +217 -0
  87. package/src/storage/supabase.ts +71 -0
  88. package/vscode-extension/README.md +107 -0
  89. package/vscode-extension/package-lock.json +1941 -0
  90. package/vscode-extension/package.json +117 -0
  91. package/vscode-extension/src/chatParticipant.ts +179 -0
  92. package/vscode-extension/src/extension.ts +262 -0
  93. package/vscode-extension/src/handlers.ts +265 -0
  94. package/vscode-extension/src/realtime.ts +302 -0
  95. package/vscode-extension/src/types.ts +41 -0
  96. package/vscode-extension/tsconfig.json +26 -0
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Watson Digest - Generate and send summary reports to watsan
3
+ *
4
+ * Creates summarized digests of unread messages and collaboration docs
5
+ * to keep the orchestrator informed.
6
+ */
7
+
8
+ import { loadConfig, Config } from './config.js';
9
+ import { loadKeyPair, decryptMessage } from './crypto.js';
10
+ import * as storage from '../storage/supabase.js';
11
+ import * as fs from 'fs';
12
+ import * as path from 'path';
13
+ import * as os from 'os';
14
+
15
+ interface DigestMessage {
16
+ from: string;
17
+ subject: string;
18
+ preview: string;
19
+ tag?: string;
20
+ timestamp: string;
21
+ }
22
+
23
+ interface DigestReport {
24
+ type: 'wake' | 'close';
25
+ agentId: string;
26
+ timestamp: string;
27
+ inbox: {
28
+ total: number;
29
+ unread: number;
30
+ };
31
+ urgentMessages: DigestMessage[];
32
+ taggedMessages: DigestMessage[];
33
+ activeCollabs: string[];
34
+ sessionDuration?: string;
35
+ }
36
+
37
+ /**
38
+ * Extract hashtag from subject line
39
+ */
40
+ function extractTag(subject: string | null): string | null {
41
+ if (!subject) return null;
42
+ const match = subject.match(/^#([a-zA-Z0-9_-]+):/);
43
+ return match ? match[1].toLowerCase() : null;
44
+ }
45
+
46
+ /**
47
+ * Get preview of message body (first 100 chars)
48
+ */
49
+ function getPreview(body: string | null): string {
50
+ if (!body) return '(no body)';
51
+ const clean = body.replace(/\n/g, ' ').trim();
52
+ return clean.length > 100 ? clean.slice(0, 97) + '...' : clean;
53
+ }
54
+
55
+ /**
56
+ * Generate a digest of unread messages
57
+ */
58
+ export async function generateDigest(
59
+ agentId: string,
60
+ type: 'wake' | 'close',
61
+ sessionDuration?: string
62
+ ): Promise<DigestReport> {
63
+ const keyPair = loadKeyPair(agentId);
64
+ const messages = await storage.getInbox(agentId, { limit: 100 });
65
+ const unreadMessages = messages.filter(m => !m.read);
66
+
67
+ const urgentMessages: DigestMessage[] = [];
68
+ const taggedMessages: DigestMessage[] = [];
69
+
70
+ for (const msg of unreadMessages.slice(0, 20)) { // Cap at 20 for digest
71
+ let subject = msg.subject || '(no subject)';
72
+ let body = msg.body || '';
73
+
74
+ // Decrypt if needed
75
+ if (msg.encrypted && keyPair && msg.ciphertext && msg.nonce && msg.senderPublicKey) {
76
+ try {
77
+ const decrypted = decryptMessage({
78
+ ciphertext: msg.ciphertext,
79
+ nonce: msg.nonce,
80
+ senderPublicKey: msg.senderPublicKey,
81
+ }, keyPair);
82
+
83
+ if (decrypted) {
84
+ const parsed = JSON.parse(decrypted);
85
+ subject = parsed.subject || subject;
86
+ body = parsed.body || body;
87
+ }
88
+ } catch {
89
+ // Keep original
90
+ }
91
+ }
92
+
93
+ const tag = extractTag(subject);
94
+ const digestMsg: DigestMessage = {
95
+ from: msg.sender || 'unknown',
96
+ subject,
97
+ preview: getPreview(body),
98
+ tag: tag || undefined,
99
+ timestamp: msg.createdAt?.toISOString() || new Date().toISOString()
100
+ };
101
+
102
+ // Categorize: urgent keywords or tagged
103
+ const isUrgent = /urgent|important|asap|critical|blocked/i.test(subject + body);
104
+ if (isUrgent) {
105
+ urgentMessages.push(digestMsg);
106
+ } else if (tag) {
107
+ taggedMessages.push(digestMsg);
108
+ }
109
+ }
110
+
111
+ // Load session for active collabs
112
+ const sessionPath = path.join(os.homedir(), '.mycmail', 'session.json');
113
+ let activeCollabs: string[] = [];
114
+ try {
115
+ if (fs.existsSync(sessionPath)) {
116
+ const session = JSON.parse(fs.readFileSync(sessionPath, 'utf-8'));
117
+ activeCollabs = session.activeCollabs || [];
118
+ }
119
+ } catch {
120
+ // Ignore
121
+ }
122
+
123
+ return {
124
+ type,
125
+ agentId,
126
+ timestamp: new Date().toISOString(),
127
+ inbox: {
128
+ total: messages.length,
129
+ unread: unreadMessages.length
130
+ },
131
+ urgentMessages,
132
+ taggedMessages,
133
+ activeCollabs,
134
+ sessionDuration
135
+ };
136
+ }
137
+
138
+ /**
139
+ * Format digest as readable text for watsan
140
+ */
141
+ export function formatDigestText(digest: DigestReport): string {
142
+ const lines: string[] = [];
143
+
144
+ lines.push(`πŸ“Š MYCM ${digest.type.toUpperCase()} DIGEST`);
145
+ lines.push(`Time: ${new Date(digest.timestamp).toLocaleString()}`);
146
+ lines.push(`Inbox: ${digest.inbox.unread} unread / ${digest.inbox.total} total`);
147
+
148
+ if (digest.sessionDuration) {
149
+ lines.push(`Session: ${digest.sessionDuration}`);
150
+ }
151
+
152
+ if (digest.urgentMessages.length > 0) {
153
+ lines.push('');
154
+ lines.push('🚨 URGENT:');
155
+ for (const msg of digest.urgentMessages.slice(0, 5)) {
156
+ lines.push(` β€’ ${msg.from}: ${msg.subject}`);
157
+ }
158
+ }
159
+
160
+ if (digest.taggedMessages.length > 0) {
161
+ lines.push('');
162
+ lines.push('🏷️ TAGGED:');
163
+ for (const msg of digest.taggedMessages.slice(0, 5)) {
164
+ lines.push(` β€’ ${msg.from}: ${msg.subject}`);
165
+ }
166
+ }
167
+
168
+ if (digest.activeCollabs.length > 0) {
169
+ lines.push('');
170
+ lines.push('πŸ“‹ ACTIVE COLLABS:');
171
+ for (const collab of digest.activeCollabs) {
172
+ lines.push(` β€’ ${collab}`);
173
+ }
174
+ }
175
+
176
+ if (digest.urgentMessages.length === 0 && digest.taggedMessages.length === 0) {
177
+ lines.push('');
178
+ lines.push('βœ… No urgent or tagged messages');
179
+ }
180
+
181
+ return lines.join('\n');
182
+ }
183
+
184
+ /**
185
+ * Send digest to watson (unencrypted) and wsan (encrypted)
186
+ */
187
+ export async function sendDigestToWatsan(digest: DigestReport): Promise<void> {
188
+ const config = loadConfig();
189
+ const content = formatDigestText(digest);
190
+ const subject = `[DIGEST] ${digest.agentId} ${digest.type} @ ${new Date().toLocaleTimeString()}`;
191
+
192
+ // Send to watson (unencrypted)
193
+ try {
194
+ await storage.sendMessage(
195
+ digest.agentId,
196
+ 'watson',
197
+ subject,
198
+ content,
199
+ { encrypted: false }
200
+ );
201
+ } catch {
202
+ // Silent fail
203
+ }
204
+
205
+ // Send to wsan (encrypted - archiver)
206
+ try {
207
+ await storage.sendMessage(
208
+ digest.agentId,
209
+ 'wsan',
210
+ subject,
211
+ content,
212
+ { encrypted: true }
213
+ );
214
+ } catch {
215
+ // Silent fail
216
+ }
217
+ }
@@ -220,6 +220,77 @@ export async function getInbox(agentId: string, options?: InboxOptions): Promise
220
220
  });
221
221
  }
222
222
 
223
+ /**
224
+ * Get inbox messages for multiple agents from Supabase
225
+ */
226
+ export async function getMultiAgentInbox(agentIds: string[], options?: InboxOptions): Promise<Message[]> {
227
+ const client = createClient();
228
+
229
+ if (!client || agentIds.length === 0) {
230
+ // For local storage, aggregate from all agents
231
+ if (agentIds.length === 0) return [];
232
+ const allMessages: Message[] = [];
233
+ for (const agentId of agentIds) {
234
+ const msgs = await local.getInbox(agentId, options);
235
+ allMessages.push(...msgs);
236
+ }
237
+ // Sort by date descending and apply limit
238
+ allMessages.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
239
+ return options?.limit ? allMessages.slice(0, options.limit) : allMessages;
240
+ }
241
+
242
+ // Build query with IN clause for multiple agents
243
+ const agentList = agentIds.map(id => `"${id}"`).join(',');
244
+ let query = `/agent_messages?to_agent=in.(${agentList})&order=created_at.desc`;
245
+
246
+ if (options?.unreadOnly) {
247
+ query += '&read=eq.false';
248
+ }
249
+
250
+ if (options?.limit) {
251
+ query += `&limit=${options.limit}`;
252
+ }
253
+
254
+ const results = await supabaseRequest<Array<{
255
+ id: string;
256
+ from_agent: string;
257
+ to_agent: string;
258
+ subject: string;
259
+ message: string;
260
+ encrypted: boolean;
261
+ read: boolean;
262
+ created_at: string;
263
+ }>>(client, query);
264
+
265
+ return results.map(r => {
266
+ // Parse encrypted message
267
+ let ciphertext, nonce, senderPublicKey, body = r.message;
268
+ if (r.encrypted && r.message) {
269
+ try {
270
+ const enc = JSON.parse(r.message);
271
+ ciphertext = enc.ciphertext;
272
+ nonce = enc.nonce;
273
+ senderPublicKey = enc.sender_public_key;
274
+ body = '';
275
+ } catch { }
276
+ }
277
+ return {
278
+ id: r.id,
279
+ sender: r.from_agent,
280
+ recipient: r.to_agent,
281
+ subject: r.subject || '',
282
+ body,
283
+ encrypted: r.encrypted,
284
+ ciphertext,
285
+ nonce,
286
+ senderPublicKey,
287
+ read: r.read,
288
+ archived: false,
289
+ createdAt: new Date(r.created_at),
290
+ };
291
+ });
292
+ }
293
+
223
294
  /**
224
295
  * Get a specific message (supports partial ID lookup)
225
296
  */
@@ -0,0 +1,107 @@
1
+ # Myceliumail Wake Agent
2
+
3
+ Real-time agent wake-up extension for VS Code and Antigravity. Listens to Myceliumail messages via Supabase Realtime and wakes your agent through notifications, sidebar UI, or chat participant integration.
4
+
5
+ ## Features
6
+
7
+ - πŸ“¬ **Real-time Notifications** - Get instant notifications when messages arrive
8
+ - πŸ’¬ **Chat Integration** - `@mycelium` chat participant for AI-powered message handling
9
+ - πŸ”„ **Auto-reconnect** - Automatic reconnection with exponential backoff
10
+ - πŸ“Š **Status Bar** - Connection status always visible
11
+
12
+ ## Setup
13
+
14
+ ### 1. Configure Settings
15
+
16
+ Open VS Code settings and configure:
17
+
18
+ ```json
19
+ {
20
+ "myceliumail.agentId": "your-agent-id",
21
+ "myceliumail.supabaseUrl": "https://your-project.supabase.co",
22
+ "myceliumail.supabaseKey": "your-anon-key"
23
+ }
24
+ ```
25
+
26
+ Or use the Settings UI:
27
+ 1. Open Command Palette (`Cmd+Shift+P`)
28
+ 2. Search "Preferences: Open Settings (UI)"
29
+ 3. Search "myceliumail"
30
+
31
+ ### 2. Verify Connection
32
+
33
+ 1. Check the status bar - should show `πŸ“¬ Myceliumail`
34
+ 2. Run command: **Myceliumail: Show Status**
35
+ 3. Send a test: **Myceliumail: Test Wake Notification**
36
+
37
+ ## Commands
38
+
39
+ | Command | Description |
40
+ |---------|-------------|
41
+ | `Myceliumail: Test Wake` | Send a test notification |
42
+ | `Myceliumail: Open Inbox` | View cached messages |
43
+ | `Myceliumail: Reconnect` | Force reconnection |
44
+ | `Myceliumail: Disconnect` | Disconnect from Supabase |
45
+ | `Myceliumail: Show Status` | Show connection status |
46
+
47
+ ## Chat Participant
48
+
49
+ Use `@mycelium` in VS Code Chat:
50
+
51
+ - `@mycelium show inbox` - List recent messages
52
+ - `@mycelium status` - Show connection status
53
+ - `@mycelium how to send` - Help with sending messages
54
+
55
+ ## Configuration Options
56
+
57
+ | Setting | Type | Default | Description |
58
+ |---------|------|---------|-------------|
59
+ | `myceliumail.agentId` | string | - | Your agent ID |
60
+ | `myceliumail.supabaseUrl` | string | - | Supabase project URL |
61
+ | `myceliumail.supabaseKey` | string | - | Supabase anon key |
62
+ | `myceliumail.enableNotifications` | boolean | true | Show notifications |
63
+ | `myceliumail.enableChatParticipant` | boolean | true | Enable @mycelium |
64
+ | `myceliumail.autoConnect` | boolean | true | Connect on startup |
65
+
66
+ ## Development
67
+
68
+ ```bash
69
+ # Install dependencies
70
+ npm install
71
+
72
+ # Compile
73
+ npm run compile
74
+
75
+ # Watch mode
76
+ npm run watch
77
+
78
+ # Package extension
79
+ npm run package
80
+ ```
81
+
82
+ ## Architecture
83
+
84
+ ```
85
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” WebSocket β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
86
+ β”‚ Supabase RT β”‚ ───────────────▢ β”‚ Wake Extension β”‚
87
+ β”‚ agent_messages β”‚ β”‚ β”‚
88
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
89
+ β”‚
90
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
91
+ β”‚ β”‚ β”‚
92
+ β–Ό β–Ό β–Ό
93
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
94
+ β”‚ Notify β”‚ β”‚ Webview β”‚ β”‚ @myceliumβ”‚
95
+ β”‚ Popup β”‚ β”‚ Panel β”‚ β”‚ Chat β”‚
96
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
97
+ ```
98
+
99
+ ## Requirements
100
+
101
+ - VS Code 1.85.0 or later (for Chat Participant API)
102
+ - Supabase account with Myceliumail tables
103
+ - Myceliumail agent configured
104
+
105
+ ## License
106
+
107
+ MIT