agentchannel 0.8.1 → 0.9.1

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 (72) hide show
  1. package/README.md +116 -77
  2. package/dist/brain.d.ts +78 -0
  3. package/dist/brain.js +271 -0
  4. package/dist/brain.js.map +1 -0
  5. package/dist/cli.js +312 -8
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config.d.ts +25 -1
  8. package/dist/config.js +104 -6
  9. package/dist/config.js.map +1 -1
  10. package/dist/crypto.d.ts +34 -4
  11. package/dist/crypto.js +42 -6
  12. package/dist/crypto.js.map +1 -1
  13. package/dist/distill.d.ts +24 -0
  14. package/dist/distill.js +404 -0
  15. package/dist/distill.js.map +1 -0
  16. package/dist/forwarder.d.ts +11 -0
  17. package/dist/forwarder.js +105 -0
  18. package/dist/forwarder.js.map +1 -0
  19. package/dist/local-store.d.ts +7 -0
  20. package/dist/local-store.js +54 -0
  21. package/dist/local-store.js.map +1 -0
  22. package/dist/mqtt-client.d.ts +11 -0
  23. package/dist/mqtt-client.js +369 -27
  24. package/dist/mqtt-client.js.map +1 -1
  25. package/dist/persistence.d.ts +23 -0
  26. package/dist/persistence.js +61 -0
  27. package/dist/persistence.js.map +1 -1
  28. package/dist/server.js +77 -3
  29. package/dist/server.js.map +1 -1
  30. package/dist/store.d.ts +3 -0
  31. package/dist/store.js +16 -2
  32. package/dist/store.js.map +1 -1
  33. package/dist/tools/brain.d.ts +2 -0
  34. package/dist/tools/brain.js +96 -0
  35. package/dist/tools/brain.js.map +1 -0
  36. package/dist/tools/channel.js +6 -6
  37. package/dist/tools/channel.js.map +1 -1
  38. package/dist/tools/get-message.js +1 -1
  39. package/dist/tools/get-message.js.map +1 -1
  40. package/dist/tools/hooks.d.ts +2 -0
  41. package/dist/tools/hooks.js +99 -0
  42. package/dist/tools/hooks.js.map +1 -0
  43. package/dist/tools/index.js +12 -0
  44. package/dist/tools/index.js.map +1 -1
  45. package/dist/tools/info.js +3 -1
  46. package/dist/tools/info.js.map +1 -1
  47. package/dist/tools/kick.d.ts +3 -0
  48. package/dist/tools/kick.js +52 -0
  49. package/dist/tools/kick.js.map +1 -0
  50. package/dist/tools/members.js +3 -3
  51. package/dist/tools/members.js.map +1 -1
  52. package/dist/tools/read.js +9 -6
  53. package/dist/tools/read.js.map +1 -1
  54. package/dist/tools/registry.d.ts +3 -0
  55. package/dist/tools/registry.js +82 -0
  56. package/dist/tools/registry.js.map +1 -0
  57. package/dist/tools/retract.d.ts +3 -0
  58. package/dist/tools/retract.js +27 -0
  59. package/dist/tools/retract.js.map +1 -0
  60. package/dist/tools/update-channel.d.ts +3 -0
  61. package/dist/tools/update-channel.js +50 -0
  62. package/dist/tools/update-channel.js.map +1 -0
  63. package/dist/types.d.ts +43 -1
  64. package/dist/web.d.ts +1 -0
  65. package/dist/web.js +91 -1
  66. package/dist/web.js.map +1 -1
  67. package/package.json +3 -2
  68. package/ui/app.js +715 -86
  69. package/ui/index.html +21 -11
  70. package/ui/marked.min.js +69 -0
  71. package/ui/mqtt.min.js +19 -0
  72. package/ui/style.css +175 -66
@@ -0,0 +1,404 @@
1
+ /**
2
+ * Distill Daemon — transforms channel messages into brain (local wiki).
3
+ *
4
+ * Subscribe = Distill = Grow. Always. No exceptions.
5
+ *
6
+ * Two-pass ingest:
7
+ * Pass 1 (no LLM): extract metadata skeleton from message fields
8
+ * Pass 2 (LLM): topic extraction, synthesis, conflict detection
9
+ *
10
+ * Single-writer: only one distill process writes to brain/.
11
+ * Lockfile at ~/.agentchannel/distill/.lock prevents concurrent runs.
12
+ */
13
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, appendFileSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { homedir } from "node:os";
16
+ import { LocalStore } from "./local-store.js";
17
+ import { loadConfig, getDistillConfig } from "./config.js";
18
+ import { fetchHistory } from "./persistence.js";
19
+ import { deriveKey, hashRoom, decrypt } from "./crypto.js";
20
+ import { ensureBrainDirs, writeTopic, readTopic, listTopics, writeChannelSynthesis, writeBrainFile, updateReferences, getBrainDir, buildSearchIndex, appendTimeline, appendDecision, } from "./brain.js";
21
+ const DISTILL_DIR = join(homedir(), ".agentchannel", "distill");
22
+ const STATE_FILE = join(DISTILL_DIR, "state.json");
23
+ const LOCK_FILE = join(DISTILL_DIR, ".lock");
24
+ const LOG_FILE = join(DISTILL_DIR, "log.jsonl");
25
+ function ensureDistillDir() {
26
+ if (!existsSync(DISTILL_DIR))
27
+ mkdirSync(DISTILL_DIR, { recursive: true });
28
+ }
29
+ function loadState() {
30
+ ensureDistillDir();
31
+ if (!existsSync(STATE_FILE))
32
+ return { last_processed: {}, last_run: 0 };
33
+ try {
34
+ return JSON.parse(readFileSync(STATE_FILE, "utf8"));
35
+ }
36
+ catch {
37
+ return { last_processed: {}, last_run: 0 };
38
+ }
39
+ }
40
+ function saveState(state) {
41
+ writeFileSync(STATE_FILE, JSON.stringify(state, null, 2) + "\n");
42
+ }
43
+ function appendLog(entry) {
44
+ ensureDistillDir();
45
+ const line = JSON.stringify({ ...entry, ts: new Date(entry.timestamp).toISOString() });
46
+ try {
47
+ appendFileSync(LOG_FILE, line + "\n");
48
+ }
49
+ catch { }
50
+ }
51
+ // ── Lock ──────────────────────────────────────────────
52
+ function acquireLock() {
53
+ ensureDistillDir();
54
+ if (existsSync(LOCK_FILE)) {
55
+ // Check if lock is stale (>10 min)
56
+ try {
57
+ const lockTime = parseInt(readFileSync(LOCK_FILE, "utf8"), 10);
58
+ if (Date.now() - lockTime < 10 * 60 * 1000)
59
+ return false;
60
+ }
61
+ catch { }
62
+ }
63
+ writeFileSync(LOCK_FILE, String(Date.now()));
64
+ return true;
65
+ }
66
+ function releaseLock() {
67
+ try {
68
+ unlinkSync(LOCK_FILE);
69
+ }
70
+ catch { }
71
+ }
72
+ function isAnthropicEndpoint(url) {
73
+ return url.includes("anthropic.com");
74
+ }
75
+ async function callLLM(prompt) {
76
+ const config = getDistillConfig();
77
+ const apiKey = config.apiKey || process.env.DISTILL_API_KEY || process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY;
78
+ const model = config.model || (process.env.ANTHROPIC_API_KEY ? "claude-haiku-4-5-20251001" : "gpt-4o-mini");
79
+ const baseUrl = config.baseUrl || config.endpoint || (process.env.ANTHROPIC_API_KEY ? "https://api.anthropic.com" : "https://api.openai.com");
80
+ if (!apiKey) {
81
+ throw new Error("No API key configured for distill. Set distill.apiKey in config, or DISTILL_API_KEY / ANTHROPIC_API_KEY / OPENAI_API_KEY env var.");
82
+ }
83
+ if (isAnthropicEndpoint(baseUrl)) {
84
+ // Anthropic Messages API
85
+ const res = await fetch(`${baseUrl}/v1/messages`, {
86
+ method: "POST",
87
+ headers: {
88
+ "Content-Type": "application/json",
89
+ "x-api-key": apiKey,
90
+ "anthropic-version": "2023-06-01",
91
+ },
92
+ body: JSON.stringify({
93
+ model,
94
+ max_tokens: 4096,
95
+ messages: [{ role: "user", content: prompt }],
96
+ }),
97
+ });
98
+ if (!res.ok) {
99
+ const text = await res.text();
100
+ throw new Error(`Anthropic API error ${res.status}: ${text}`);
101
+ }
102
+ const data = await res.json();
103
+ return data.content?.[0]?.text || "";
104
+ }
105
+ else {
106
+ // OpenAI-compatible API (OpenAI, Ollama, local models, etc.)
107
+ const res = await fetch(`${baseUrl}/v1/chat/completions`, {
108
+ method: "POST",
109
+ headers: {
110
+ "Content-Type": "application/json",
111
+ "Authorization": `Bearer ${apiKey}`,
112
+ },
113
+ body: JSON.stringify({
114
+ model,
115
+ max_tokens: 4096,
116
+ messages: [{ role: "user", content: prompt }],
117
+ }),
118
+ });
119
+ if (!res.ok) {
120
+ const text = await res.text();
121
+ throw new Error(`LLM API error ${res.status}: ${text}`);
122
+ }
123
+ const data = await res.json();
124
+ return data.choices?.[0]?.message?.content || "";
125
+ }
126
+ }
127
+ function extractMetadata(messages) {
128
+ const senders = new Map();
129
+ const tags = new Map();
130
+ const topics = [];
131
+ const replyMap = new Map(); // msg id -> parent id
132
+ for (const msg of messages) {
133
+ senders.set(msg.sender, (senders.get(msg.sender) || 0) + 1);
134
+ if (msg.tags) {
135
+ for (const tag of msg.tags) {
136
+ tags.set(tag, (tags.get(tag) || 0) + 1);
137
+ }
138
+ }
139
+ if (msg.subject)
140
+ topics.push(msg.subject);
141
+ if (msg.replyTo)
142
+ replyMap.set(msg.id, msg.replyTo);
143
+ }
144
+ // Build reply chains
145
+ const replyChains = [];
146
+ const visited = new Set();
147
+ for (const [id, parentId] of replyMap) {
148
+ if (visited.has(id))
149
+ continue;
150
+ const chain = [parentId, id];
151
+ visited.add(id);
152
+ replyChains.push(chain);
153
+ }
154
+ return {
155
+ senders,
156
+ tags,
157
+ topics,
158
+ replyChains,
159
+ timeRange: {
160
+ start: messages[0]?.timestamp || 0,
161
+ end: messages[messages.length - 1]?.timestamp || 0,
162
+ },
163
+ };
164
+ }
165
+ // ── Pass 2: LLM synthesis ─────────────────────────────
166
+ async function synthesizeTopics(channel, messages, existingTopics) {
167
+ const msgTexts = messages.map((m) => {
168
+ const date = new Date(m.timestamp).toISOString().slice(0, 10);
169
+ const tags = m.tags?.length ? ` [${m.tags.join(", ")}]` : "";
170
+ return `[${date}] @${m.sender}${tags}: ${m.content}`;
171
+ }).join("\n\n");
172
+ const existingList = existingTopics.length > 0
173
+ ? `\nExisting topic pages (update if relevant, do not duplicate):\n${existingTopics.map((e) => `- ${e}`).join("\n")}`
174
+ : "";
175
+ // Build brain context: existing topics tell the LLM what we care about
176
+ const brainContext = existingTopics.length > 0
177
+ ? `\nOur brain already tracks these topics (this is what we care about):\n${existingTopics.map((e) => `- ${e}`).join("\n")}\n`
178
+ : "";
179
+ const prompt = `You are a knowledge distillation agent. Your job is to grow our shared brain by extracting valuable knowledge from messages.
180
+ ${brainContext}
181
+ Messages from #${channel}:
182
+ ${msgTexts}
183
+
184
+ Respond with EXACTLY this JSON structure (no other text):
185
+ {
186
+ "topics": [
187
+ {
188
+ "slug": "lowercase-hyphenated-name",
189
+ "content": "Full markdown content for topic page including YAML frontmatter with aliases, sources, last_updated, created fields"
190
+ }
191
+ ],
192
+ "synthesis": "A markdown summary of the key themes and activity in this batch of messages (2-4 paragraphs)",
193
+ "timeline": [
194
+ {"date": "YYYY-MM-DD", "summary": "One-line description of what happened"}
195
+ ],
196
+ "decisions": [
197
+ {"topic": "short topic name", "summary": "what was decided", "rationale": "why"}
198
+ ]
199
+ }
200
+
201
+ Rules:
202
+ - Only extract topics that are EXPLICITLY discussed, do not infer or speculate
203
+ - Topic slugs: lowercase, hyphenated, descriptive (e.g. "epoch-rotation", "hkdf-sha256")
204
+ - Topic content: include YAML frontmatter with aliases (alternative names), sources (channel names)
205
+ - Topic pages should be under 2000 tokens
206
+ - Synthesis: factual summary of the batch, not opinions
207
+ - Timeline: only significant events, not every message
208
+ - Decisions: only explicit decisions, not suggestions or proposals
209
+ - If no topics/decisions found, return empty arrays
210
+ - For external content (RSS feeds, news, links): only distill items that are RELEVANT to our existing topics or could directly benefit our work. Skip generic news that has no connection to what we care about.
211
+ - When distilling external content, focus on the actionable insight, not the full article — what should we know or do differently because of this?`;
212
+ try {
213
+ const response = await callLLM(prompt);
214
+ // Parse JSON from response
215
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
216
+ if (!jsonMatch)
217
+ return { topics: [], synthesis: "", timeline: [], decisions: [] };
218
+ return JSON.parse(jsonMatch[0]);
219
+ }
220
+ catch (err) {
221
+ appendLog({ timestamp: Date.now(), channel, action: "llm_error", details: String(err) });
222
+ return { topics: [], synthesis: "", timeline: [], decisions: [] };
223
+ }
224
+ }
225
+ // ── Main distill loop ─────────────────────────────────
226
+ async function distillChannel(channel, subchannel, messages) {
227
+ if (messages.length === 0)
228
+ return 0;
229
+ const channelId = subchannel ? `${channel}/${subchannel}` : channel;
230
+ // Pass 1: metadata
231
+ const meta = extractMetadata(messages);
232
+ appendLog({
233
+ timestamp: Date.now(),
234
+ channel: channelId,
235
+ action: "pass1",
236
+ details: `${messages.length} msgs, ${meta.senders.size} senders, ${meta.topics.length} topics`,
237
+ });
238
+ // Pass 2: LLM synthesis
239
+ const existing = listTopics();
240
+ const result = await synthesizeTopics(channelId, messages, existing);
241
+ // Write topics
242
+ for (const topic of result.topics) {
243
+ const existingContent = readTopic(topic.slug);
244
+ if (existingContent) {
245
+ // Merge: LLM already knows about existing topics via prompt
246
+ writeTopic(topic.slug, topic.content);
247
+ }
248
+ else {
249
+ writeTopic(topic.slug, topic.content);
250
+ }
251
+ appendLog({ timestamp: Date.now(), channel: channelId, action: "topic", details: topic.slug });
252
+ }
253
+ // Write channel synthesis
254
+ if (result.synthesis) {
255
+ const header = `# #${channelId} — Current\n\nLast updated: ${new Date().toISOString().slice(0, 10)}\n\n`;
256
+ writeChannelSynthesis(channelId, header + result.synthesis);
257
+ }
258
+ // Update timeline (monthly archive + latest.md)
259
+ if (result.timeline.length > 0) {
260
+ appendTimeline(result.timeline.map((t) => ({ ...t, channel: channelId })));
261
+ }
262
+ // Update decisions (monthly archive + latest.md)
263
+ if (result.decisions.length > 0) {
264
+ const date = new Date().toISOString().slice(0, 10);
265
+ for (const d of result.decisions) {
266
+ appendDecision({ date, topic: d.topic, summary: d.summary, rationale: d.rationale, channel: channelId });
267
+ }
268
+ }
269
+ return result.topics.length;
270
+ }
271
+ // ── Rebuild index and xref ────────────────────────────
272
+ function rebuildIndex() {
273
+ const topics = listTopics();
274
+ const entries = [];
275
+ const xrefs = {};
276
+ for (const slug of topics) {
277
+ const content = readTopic(slug);
278
+ if (!content)
279
+ continue;
280
+ // Extract sources from frontmatter
281
+ const sourcesMatch = content.match(/sources:\s*\[([^\]]*)\]/);
282
+ const sources = sourcesMatch
283
+ ? sourcesMatch[1].split(",").map((s) => s.trim().replace(/['"]/g, ""))
284
+ : ["unknown"];
285
+ // Use slug as question basis
286
+ const title = slug.replace(/-/g, " ");
287
+ entries.push({ question: `What is ${title}?`, slug, source: sources[0] || "unknown" });
288
+ // Build xref
289
+ xrefs[slug] = sources;
290
+ }
291
+ // Write index
292
+ const lines = ["# Brain Index\n"];
293
+ for (const e of entries.sort((a, b) => a.slug.localeCompare(b.slug))) {
294
+ lines.push(`- ${e.question} → [${e.slug}](topics/${e.slug}.md) (from #${e.source})`);
295
+ }
296
+ lines.push(`\n_${entries.length} topics, last rebuilt: ${new Date().toISOString().slice(0, 10)}_\n`);
297
+ writeBrainFile("index.md", lines.join("\n"));
298
+ // Write xref
299
+ updateReferences(xrefs);
300
+ }
301
+ // ── Public API ────────────────────────────────────────
302
+ export async function runDistillOnce() {
303
+ const distillConfig = getDistillConfig();
304
+ if (!distillConfig.enabled) {
305
+ return { channels: 0, topics: 0 };
306
+ }
307
+ if (!acquireLock()) {
308
+ throw new Error("Another distill process is running. Remove ~/.agentchannel/distill/.lock if stale.");
309
+ }
310
+ try {
311
+ ensureBrainDirs();
312
+ const config = loadConfig();
313
+ const state = loadState();
314
+ const localStore = new LocalStore();
315
+ let totalTopics = 0;
316
+ let channelsProcessed = 0;
317
+ for (const ch of config.channels) {
318
+ if (ch.subchannel)
319
+ continue; // Process main channels only, subchannels included via their parent
320
+ const channelId = ch.channel;
321
+ const since = state.last_processed[channelId] || 0;
322
+ // Try local store first, fall back to archive
323
+ let messages = localStore.readMessages(ch.channel, undefined, since);
324
+ if (messages.length === 0) {
325
+ // Fall back to cloud archive — decrypt ciphertext locally
326
+ try {
327
+ const hash = hashRoom(ch.key);
328
+ const key = deriveKey(ch.key);
329
+ const rows = await fetchHistory(hash, since, 200);
330
+ for (const row of rows) {
331
+ try {
332
+ const encrypted = JSON.parse(row.ciphertext);
333
+ const decrypted = decrypt(encrypted, key);
334
+ const msg = JSON.parse(decrypted);
335
+ msg.channel = ch.channel;
336
+ if (!msg.type)
337
+ msg.type = "chat";
338
+ if (msg.type === "channel_meta" || msg.type === "retraction")
339
+ continue;
340
+ messages.push(msg);
341
+ }
342
+ catch { }
343
+ }
344
+ }
345
+ catch { }
346
+ }
347
+ if (messages.length === 0)
348
+ continue;
349
+ // Filter out system/meta messages
350
+ messages = messages.filter((m) => m.type === "chat" || !m.type);
351
+ const topicCount = await distillChannel(ch.channel, undefined, messages);
352
+ totalTopics += topicCount;
353
+ channelsProcessed++;
354
+ // Update state
355
+ const latestTimestamp = messages[messages.length - 1]?.timestamp || since;
356
+ state.last_processed[channelId] = latestTimestamp;
357
+ }
358
+ // Rebuild global index, references, and search index
359
+ if (totalTopics > 0) {
360
+ rebuildIndex();
361
+ buildSearchIndex();
362
+ }
363
+ state.last_run = Date.now();
364
+ saveState(state);
365
+ appendLog({
366
+ timestamp: Date.now(),
367
+ channel: "*",
368
+ action: "complete",
369
+ details: `${channelsProcessed} channels, ${totalTopics} topics`,
370
+ });
371
+ return { channels: channelsProcessed, topics: totalTopics };
372
+ }
373
+ finally {
374
+ releaseLock();
375
+ }
376
+ }
377
+ export async function runDistillWatch(intervalMs = 5 * 60 * 1000) {
378
+ console.log(`Distill daemon started (interval: ${intervalMs / 1000}s). Brain: ${getBrainDir()}`);
379
+ while (true) {
380
+ try {
381
+ const result = await runDistillOnce();
382
+ if (result.topics > 0) {
383
+ console.log(`Distilled ${result.topics} topics from ${result.channels} channels`);
384
+ }
385
+ }
386
+ catch (err) {
387
+ console.error("Distill error:", err);
388
+ }
389
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
390
+ }
391
+ }
392
+ export function getDistillStatus() {
393
+ const config = getDistillConfig();
394
+ const state = loadState();
395
+ const topics = listTopics();
396
+ return {
397
+ enabled: config.enabled,
398
+ lastRun: state.last_run,
399
+ brainDir: getBrainDir(),
400
+ topicCount: topics.length,
401
+ channelsProcessed: Object.keys(state.last_processed),
402
+ };
403
+ }
404
+ //# sourceMappingURL=distill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"distill.js","sourceRoot":"","sources":["../src/distill.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Message } from "./types.js";
2
+ import type { AgentChatClient } from "./mqtt-client.js";
3
+ export declare class MessageForwarder {
4
+ private client;
5
+ constructor(client: AgentChatClient);
6
+ onMessage(msg: Message): void;
7
+ private matches;
8
+ private postWebhook;
9
+ private sendAck;
10
+ private sendPendingNotice;
11
+ }
@@ -0,0 +1,105 @@
1
+ import { loadConfig } from "./config.js";
2
+ export class MessageForwarder {
3
+ client;
4
+ constructor(client) {
5
+ this.client = client;
6
+ }
7
+ onMessage(msg) {
8
+ // Skip system messages and unsigned messages
9
+ if (msg.type === "system" || msg.type === "channel_meta")
10
+ return;
11
+ if (!msg.signature || !msg.senderKey)
12
+ return;
13
+ // Only trigger for trusted senders (TOFU or verified)
14
+ if (msg.trustLevel !== "tofu" && msg.trustLevel !== "verified")
15
+ return;
16
+ // Skip own messages (prevent loops)
17
+ if (msg.senderKey === this.client.getFingerprint())
18
+ return;
19
+ const config = loadConfig();
20
+ for (const wh of config.webhooks || []) {
21
+ if (this.matches(msg, wh)) {
22
+ this.postWebhook(wh.url, msg, "webhook");
23
+ }
24
+ }
25
+ for (const hf of config.handoffs || []) {
26
+ if (this.matches(msg, hf)) {
27
+ const mode = hf.mode || "ask";
28
+ if (mode === "auto") {
29
+ // Auto mode: POST + ACK immediately
30
+ this.postWebhook(hf.url, msg, "handoff");
31
+ this.sendAck(msg, hf.output);
32
+ }
33
+ else {
34
+ // Ask mode: surface request in channel, wait for approval
35
+ this.sendPendingNotice(msg, hf);
36
+ }
37
+ }
38
+ }
39
+ }
40
+ matches(msg, filter) {
41
+ // Channel must match
42
+ if (msg.channel !== filter.channel)
43
+ return false;
44
+ if (filter.subchannel && msg.subchannel !== filter.subchannel)
45
+ return false;
46
+ // Tag filter: message must have at least one matching tag
47
+ if (filter.tags && filter.tags.length > 0) {
48
+ if (!msg.tags || !msg.tags.some((t) => filter.tags.includes(t)))
49
+ return false;
50
+ }
51
+ // Sender whitelist
52
+ if (filter.senders && filter.senders.length > 0) {
53
+ if (!filter.senders.includes(msg.senderKey))
54
+ return false;
55
+ }
56
+ return true;
57
+ }
58
+ async postWebhook(url, msg, type) {
59
+ try {
60
+ await fetch(url, {
61
+ method: "POST",
62
+ headers: { "Content-Type": "application/json" },
63
+ body: JSON.stringify({
64
+ type,
65
+ message: {
66
+ id: msg.id,
67
+ channel: msg.channel,
68
+ subchannel: msg.subchannel,
69
+ sender: msg.sender,
70
+ senderKey: msg.senderKey,
71
+ content: msg.content,
72
+ subject: msg.subject,
73
+ tags: msg.tags,
74
+ timestamp: msg.timestamp,
75
+ replyTo: msg.replyTo,
76
+ },
77
+ }),
78
+ signal: AbortSignal.timeout(10_000),
79
+ });
80
+ }
81
+ catch {
82
+ // Fire and forget — don't crash the server
83
+ }
84
+ }
85
+ async sendAck(msg, output) {
86
+ try {
87
+ const target = output || (msg.subchannel ? `${msg.channel}/${msg.subchannel}` : msg.channel);
88
+ await this.client.send(`Acknowledged handoff from @${msg.sender}: ${msg.subject || msg.content.slice(0, 100)}`, target, { tags: ["handoff-ack"], subject: `ACK: ${msg.subject || msg.id}` });
89
+ }
90
+ catch {
91
+ // Best effort
92
+ }
93
+ }
94
+ async sendPendingNotice(msg, hf) {
95
+ try {
96
+ const target = hf.output || (msg.subchannel ? `${msg.channel}/${msg.subchannel}` : msg.channel);
97
+ await this.client.send(`Handoff request from @${msg.sender} (${msg.senderKey}): ${msg.subject || msg.content.slice(0, 100)}\n` +
98
+ `Hook: ${hf.id} | Mode: ask — awaiting approval`, target, { tags: ["handoff-pending"], subject: `PENDING: ${msg.subject || msg.id}` });
99
+ }
100
+ catch {
101
+ // Best effort
102
+ }
103
+ }
104
+ }
105
+ //# sourceMappingURL=forwarder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forwarder.js","sourceRoot":"","sources":["../src/forwarder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { Message } from "./types.js";
2
+ export declare class LocalStore {
3
+ private written;
4
+ appendMessage(msg: Message): void;
5
+ readMessages(channel: string, subchannel?: string, since?: number, limit?: number): Message[];
6
+ hasMessage(id: string): boolean;
7
+ }
@@ -0,0 +1,54 @@
1
+ import { existsSync, mkdirSync, appendFileSync, readFileSync, readdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ const MESSAGES_DIR = join(homedir(), "agentchannel", "messages");
5
+ function channelDir(channel, subchannel) {
6
+ const name = subchannel ? `${channel}.${subchannel}` : channel;
7
+ return join(MESSAGES_DIR, name);
8
+ }
9
+ function monthFile(channel, subchannel, date) {
10
+ const yyyy = date.getFullYear();
11
+ const mm = String(date.getMonth() + 1).padStart(2, "0");
12
+ const name = subchannel ? `${channel}.${subchannel}` : channel;
13
+ return join(channelDir(channel, subchannel), `${name}.${yyyy}-${mm}.jsonl`);
14
+ }
15
+ export class LocalStore {
16
+ written = new Set(); // dedup by msg id
17
+ appendMessage(msg) {
18
+ if (this.written.has(msg.id))
19
+ return;
20
+ const dir = channelDir(msg.channel, msg.subchannel);
21
+ if (!existsSync(dir))
22
+ mkdirSync(dir, { recursive: true });
23
+ const file = monthFile(msg.channel, msg.subchannel, new Date(msg.timestamp));
24
+ appendFileSync(file, JSON.stringify(msg) + "\n");
25
+ this.written.add(msg.id);
26
+ }
27
+ readMessages(channel, subchannel, since, limit) {
28
+ const dir = channelDir(channel, subchannel);
29
+ if (!existsSync(dir))
30
+ return [];
31
+ const files = readdirSync(dir)
32
+ .filter((f) => f.endsWith(".jsonl"))
33
+ .sort(); // chronological by YYYY-MM
34
+ const msgs = [];
35
+ for (const file of files) {
36
+ const lines = readFileSync(join(dir, file), "utf8").split("\n").filter(Boolean);
37
+ for (const line of lines) {
38
+ try {
39
+ const msg = JSON.parse(line);
40
+ if (since && msg.timestamp <= since)
41
+ continue;
42
+ msgs.push(msg);
43
+ }
44
+ catch { }
45
+ }
46
+ }
47
+ msgs.sort((a, b) => a.timestamp - b.timestamp);
48
+ return limit ? msgs.slice(-limit) : msgs;
49
+ }
50
+ hasMessage(id) {
51
+ return this.written.has(id);
52
+ }
53
+ }
54
+ //# sourceMappingURL=local-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-store.js","sourceRoot":"","sources":["../src/local-store.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { MessageStore } from "./store.js";
2
+ import { LocalStore } from "./local-store.js";
2
3
  import type { ChatConfig, SingleChannelConfig, Message, ChannelMeta } from "./types.js";
3
4
  export declare class AgentChatClient {
4
5
  private client;
@@ -7,6 +8,7 @@ export declare class AgentChatClient {
7
8
  private name;
8
9
  private broker;
9
10
  readonly store: MessageStore;
11
+ readonly localStore: LocalStore;
10
12
  private onMessage?;
11
13
  private onMeta?;
12
14
  private channelMeta;
@@ -17,6 +19,8 @@ export declare class AgentChatClient {
17
19
  get memberName(): string;
18
20
  getFingerprint(): string;
19
21
  getChannelKeyString(channel: string): string | undefined;
22
+ getName(): string;
23
+ getMemberCount(channel: string): number;
20
24
  private msgTopic;
21
25
  private presTopic;
22
26
  connect(): Promise<void>;
@@ -44,6 +48,13 @@ export declare class AgentChatClient {
44
48
  setOnMeta(handler: (channel: string, meta: ChannelMeta) => void): void;
45
49
  getMeta(channel: string): ChannelMeta | undefined;
46
50
  publishMeta(channelName: string, meta: ChannelMeta): Promise<void>;
51
+ private broadcastEpochBump;
52
+ removeMember(channelName: string, targetFingerprint: string, opts?: {
53
+ silent?: boolean;
54
+ reason?: string;
55
+ }): Promise<void>;
56
+ rotateChannel(channelName: string): Promise<void>;
57
+ retractMessage(messageId: string, channelName?: string, reason?: string): Promise<Message>;
47
58
  joinDm(theirFingerprint: string): Promise<string>;
48
59
  sendDm(theirFingerprint: string, content: string, opts?: {
49
60
  subject?: string;