beecork 1.4.11 → 1.6.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 (138) hide show
  1. package/dist/capabilities/index.d.ts +1 -1
  2. package/dist/capabilities/index.js +1 -1
  3. package/dist/capabilities/manager.js +13 -9
  4. package/dist/capabilities/packs.js +3 -1
  5. package/dist/channels/admin.d.ts +10 -0
  6. package/dist/channels/admin.js +20 -0
  7. package/dist/channels/command-handler.d.ts +2 -10
  8. package/dist/channels/command-handler.js +90 -84
  9. package/dist/channels/discord.d.ts +4 -9
  10. package/dist/channels/discord.js +59 -42
  11. package/dist/channels/index.d.ts +1 -1
  12. package/dist/channels/loader.js +13 -4
  13. package/dist/channels/pipeline.js +14 -5
  14. package/dist/channels/registry.d.ts +17 -1
  15. package/dist/channels/registry.js +33 -4
  16. package/dist/channels/send-helpers.d.ts +19 -0
  17. package/dist/channels/send-helpers.js +21 -0
  18. package/dist/channels/telegram.d.ts +21 -14
  19. package/dist/channels/telegram.js +214 -104
  20. package/dist/channels/types.d.ts +13 -38
  21. package/dist/channels/voice-state.d.ts +29 -0
  22. package/dist/channels/voice-state.js +45 -0
  23. package/dist/channels/webhook.d.ts +2 -5
  24. package/dist/channels/webhook.js +88 -29
  25. package/dist/channels/whatsapp.d.ts +9 -7
  26. package/dist/channels/whatsapp.js +141 -100
  27. package/dist/cli/capabilities.js +4 -4
  28. package/dist/cli/channel.js +16 -6
  29. package/dist/cli/commands.js +12 -9
  30. package/dist/cli/doctor.js +85 -27
  31. package/dist/cli/handoff.d.ts +7 -14
  32. package/dist/cli/handoff.js +9 -44
  33. package/dist/cli/mcp.js +5 -5
  34. package/dist/cli/media.js +21 -8
  35. package/dist/cli/setup.js +9 -8
  36. package/dist/cli/store.js +29 -12
  37. package/dist/config.d.ts +5 -1
  38. package/dist/config.js +20 -22
  39. package/dist/daemon.js +113 -51
  40. package/dist/dashboard/html.js +100 -20
  41. package/dist/dashboard/routes.d.ts +17 -0
  42. package/dist/dashboard/routes.js +623 -0
  43. package/dist/dashboard/server.js +38 -489
  44. package/dist/db/connection.d.ts +29 -0
  45. package/dist/db/connection.js +37 -0
  46. package/dist/db/index.js +43 -11
  47. package/dist/db/migrations.js +114 -22
  48. package/dist/delegation/manager.js +10 -4
  49. package/dist/index.js +39 -59
  50. package/dist/knowledge/manager.js +26 -12
  51. package/dist/mcp/handlers.d.ts +37 -0
  52. package/dist/mcp/handlers.js +520 -0
  53. package/dist/mcp/server.js +44 -858
  54. package/dist/mcp/tool-definitions.d.ts +1225 -0
  55. package/dist/mcp/tool-definitions.js +412 -0
  56. package/dist/mcp/validate.d.ts +23 -0
  57. package/dist/mcp/validate.js +65 -0
  58. package/dist/media/factory.js +18 -14
  59. package/dist/media/generators/dall-e.js +2 -2
  60. package/dist/media/generators/kling.js +4 -4
  61. package/dist/media/generators/lyria.js +1 -1
  62. package/dist/media/generators/nano-banana.d.ts +1 -1
  63. package/dist/media/generators/nano-banana.js +2 -2
  64. package/dist/media/generators/poll-util.js +4 -4
  65. package/dist/media/generators/recraft.js +3 -3
  66. package/dist/media/generators/runway.js +4 -4
  67. package/dist/media/generators/stable-diffusion.js +2 -2
  68. package/dist/media/generators/veo.js +1 -1
  69. package/dist/media/index.d.ts +2 -7
  70. package/dist/media/index.js +2 -2
  71. package/dist/media/store.d.ts +7 -0
  72. package/dist/media/store.js +18 -4
  73. package/dist/media/types.d.ts +22 -0
  74. package/dist/notifications/index.d.ts +2 -4
  75. package/dist/notifications/index.js +6 -19
  76. package/dist/notifications/ntfy.js +3 -3
  77. package/dist/observability/analytics.d.ts +1 -1
  78. package/dist/observability/analytics.js +41 -16
  79. package/dist/projects/index.d.ts +3 -2
  80. package/dist/projects/index.js +2 -2
  81. package/dist/projects/manager.d.ts +1 -7
  82. package/dist/projects/manager.js +66 -42
  83. package/dist/projects/router.d.ts +12 -0
  84. package/dist/projects/router.js +98 -45
  85. package/dist/service/install.js +15 -5
  86. package/dist/service/windows.js +1 -1
  87. package/dist/session/budget-guard.d.ts +20 -0
  88. package/dist/session/budget-guard.js +31 -0
  89. package/dist/session/circuit-breaker.d.ts +5 -3
  90. package/dist/session/circuit-breaker.js +45 -20
  91. package/dist/session/context-compactor.d.ts +32 -0
  92. package/dist/session/context-compactor.js +45 -0
  93. package/dist/session/context-monitor.js +2 -2
  94. package/dist/session/handoff.d.ts +21 -0
  95. package/dist/session/handoff.js +50 -0
  96. package/dist/session/manager.d.ts +21 -5
  97. package/dist/session/manager.js +166 -153
  98. package/dist/session/memory-store.d.ts +29 -0
  99. package/dist/session/memory-store.js +45 -0
  100. package/dist/session/message-queue.d.ts +28 -0
  101. package/dist/session/message-queue.js +52 -0
  102. package/dist/session/pending-dispatcher.d.ts +31 -0
  103. package/dist/session/pending-dispatcher.js +120 -0
  104. package/dist/session/pending-store.d.ts +60 -0
  105. package/dist/session/pending-store.js +118 -0
  106. package/dist/session/stale-session.d.ts +31 -0
  107. package/dist/session/stale-session.js +45 -0
  108. package/dist/session/subprocess.d.ts +3 -0
  109. package/dist/session/subprocess.js +54 -11
  110. package/dist/session/tab-store.d.ts +28 -0
  111. package/dist/session/tab-store.js +78 -0
  112. package/dist/tasks/scheduler.d.ts +13 -0
  113. package/dist/tasks/scheduler.js +97 -18
  114. package/dist/tasks/store.js +26 -12
  115. package/dist/timeline/logger.js +3 -1
  116. package/dist/timeline/query.js +15 -5
  117. package/dist/types.d.ts +49 -9
  118. package/dist/util/auto-heal.js +15 -5
  119. package/dist/util/install-info.js +3 -1
  120. package/dist/util/logger.d.ts +1 -1
  121. package/dist/util/logger.js +63 -24
  122. package/dist/util/paths.d.ts +2 -0
  123. package/dist/util/paths.js +16 -3
  124. package/dist/util/rate-limiter.js +8 -0
  125. package/dist/util/retry.js +1 -1
  126. package/dist/util/text.d.ts +21 -1
  127. package/dist/util/text.js +38 -8
  128. package/dist/voice/index.js +5 -1
  129. package/dist/voice/stt.js +14 -6
  130. package/dist/voice/tts.js +1 -1
  131. package/dist/watchers/scheduler.js +11 -5
  132. package/package.json +6 -1
  133. package/dist/session/tool-classifier.d.ts +0 -4
  134. package/dist/session/tool-classifier.js +0 -56
  135. package/dist/users/index.d.ts +0 -2
  136. package/dist/users/index.js +0 -1
  137. package/dist/users/service.d.ts +0 -17
  138. package/dist/users/service.js +0 -46
@@ -37,7 +37,6 @@ export async function loadCommunityChannels(ctx) {
37
37
  continue;
38
38
  if (!allowlist.includes(dir))
39
39
  continue;
40
- const channelName = dir.slice(CHANNEL_PREFIX.length);
41
40
  const pkgPath = path.join(searchPath, dir);
42
41
  try {
43
42
  // Read package.json to find the main entry
@@ -48,8 +47,15 @@ export async function loadCommunityChannels(ctx) {
48
47
  logger.warn(`Community channel ${dir}: entry point not found at ${entryPath}`);
49
48
  continue;
50
49
  }
51
- // Dynamic import — community channels run with full daemon access
52
- logger.warn(`Loading community channel from ${dir} ensure you trust this package`);
50
+ // Dynamic import — community channels run with full daemon access:
51
+ // they can read every config token (Telegram, Discord, WhatsApp,
52
+ // notification provider keys) AND every environment variable the
53
+ // daemon inherits (including SSH-agent sockets, GitHub tokens,
54
+ // anything sourced from your shell profile). A compromised
55
+ // community-channel package is effectively a root credential drop.
56
+ // Pin versions in package.json, audit before opting in, and treat
57
+ // every entry in `communityChannels` as a trust anchor.
58
+ logger.warn(`Loading community channel "${dir}" — this package will have FULL access to your channel tokens (Telegram/Discord/WhatsApp) and every environment variable inherited by the daemon (including SSH keys). Only enable if you trust the package author and have audited the version.`);
53
59
  const module = await import(entryPath);
54
60
  const ChannelClass = module.default || module[Object.keys(module)[0]];
55
61
  if (!ChannelClass || typeof ChannelClass !== 'function') {
@@ -58,7 +64,10 @@ export async function loadCommunityChannels(ctx) {
58
64
  }
59
65
  const instance = new ChannelClass(ctx);
60
66
  // Validate it implements Channel interface (duck typing)
61
- if (!instance.id || !instance.name || typeof instance.start !== 'function' || typeof instance.stop !== 'function') {
67
+ if (!instance.id ||
68
+ !instance.name ||
69
+ typeof instance.start !== 'function' ||
70
+ typeof instance.stop !== 'function') {
62
71
  logger.warn(`Community channel ${dir}: does not implement Channel interface`);
63
72
  continue;
64
73
  }
@@ -22,9 +22,20 @@ import { logger } from '../util/logger.js';
22
22
  export async function processInboundMessage(opts) {
23
23
  const { text, media, channelId, tabManager, voiceReplyMode, ttsProvider, userId, sendProgress, overrideTabName, onTextChunk, } = opts;
24
24
  // 1. Parse tab name and prompt from the message text
25
- let { tabName, prompt: rawPrompt } = parseTabMessage(text);
26
- // Allow channel to override tab name (group tabs, thread tabs, etc.)
25
+ const parsed = parseTabMessage(text);
26
+ const rawPrompt = parsed.prompt;
27
+ let tabName = parsed.tabName;
28
+ // Allow channel to override tab name (group tabs, thread tabs, etc.).
29
+ // Validate centrally so each channel doesn't need its own check — keeps the
30
+ // CLAUDE.md "validation is centralized" rule honest. `default` is allowed.
27
31
  if (overrideTabName) {
32
+ if (overrideTabName !== 'default') {
33
+ const { validateTabName } = await import('../config.js');
34
+ const overrideErr = validateTabName(overrideTabName);
35
+ if (overrideErr) {
36
+ return { responseText: `Invalid tab name: ${overrideErr}`, tabName, isError: true };
37
+ }
38
+ }
28
39
  tabName = overrideTabName;
29
40
  }
30
41
  if (!rawPrompt && media.length === 0) {
@@ -56,9 +67,7 @@ export async function processInboundMessage(opts) {
56
67
  progressTracker.stop();
57
68
  // 6. Build response text
58
69
  const isError = result.error;
59
- const responseText = isError
60
- ? `Error: ${result.text}`
61
- : result.text || '(empty response)';
70
+ const responseText = isError ? `Error: ${result.text}` : result.text || '(empty response)';
62
71
  // 7. TTS voice reply (shared logic)
63
72
  let audioPath;
64
73
  let voiceOnly = false;
@@ -1,4 +1,5 @@
1
1
  import type { Channel } from './types.js';
2
+ import type { MediaAttachment } from '../types.js';
2
3
  /**
3
4
  * ChannelRegistry manages the lifecycle of all channels.
4
5
  * Channels register themselves, and the registry handles start/stop/broadcast.
@@ -7,7 +8,12 @@ export declare class ChannelRegistry {
7
8
  private channels;
8
9
  /** Register a channel */
9
10
  register(channel: Channel): void;
10
- /** Start all registered channels */
11
+ /**
12
+ * Start all registered channels in parallel. Each channel's start() is an
13
+ * independent network handshake (Telegram getMe, Discord login, WhatsApp
14
+ * Baileys boot) — running them sequentially adds 2-5s to cold start with no
15
+ * coordination benefit.
16
+ */
11
17
  start(): Promise<void>;
12
18
  /** Stop all channels */
13
19
  stop(): void;
@@ -17,4 +23,14 @@ export declare class ChannelRegistry {
17
23
  getAll(): Channel[];
18
24
  /** Broadcast a notification to all channels */
19
25
  broadcastNotify(text: string, urgent?: boolean): Promise<void>;
26
+ /**
27
+ * Broadcast a media attachment to every channel that supports it. Used by
28
+ * the pending-message dispatcher to deliver media queued by
29
+ * `beecork_send_media` and the `beecork_generate_*` tools — without this,
30
+ * those tools queue a row whose JSON envelope is never rendered.
31
+ *
32
+ * Channels that don't implement broadcastMedia get a sendNotification
33
+ * fallback so the user at least learns a file was generated.
34
+ */
35
+ broadcastMedia(media: MediaAttachment): Promise<void>;
20
36
  }
@@ -13,9 +13,14 @@ export class ChannelRegistry {
13
13
  this.channels.set(channel.id, channel);
14
14
  logger.info(`Channel registered: ${channel.name} (${channel.id})`);
15
15
  }
16
- /** Start all registered channels */
16
+ /**
17
+ * Start all registered channels in parallel. Each channel's start() is an
18
+ * independent network handshake (Telegram getMe, Discord login, WhatsApp
19
+ * Baileys boot) — running them sequentially adds 2-5s to cold start with no
20
+ * coordination benefit.
21
+ */
17
22
  async start() {
18
- for (const [id, channel] of this.channels) {
23
+ await Promise.all(Array.from(this.channels.entries()).map(async ([id, channel]) => {
19
24
  try {
20
25
  await channel.start();
21
26
  logger.info(`Channel started: ${channel.name}`);
@@ -23,7 +28,7 @@ export class ChannelRegistry {
23
28
  catch (err) {
24
29
  logger.error(`Failed to start channel ${id}:`, err);
25
30
  }
26
- }
31
+ }));
27
32
  }
28
33
  /** Stop all channels */
29
34
  stop() {
@@ -46,6 +51,30 @@ export class ChannelRegistry {
46
51
  }
47
52
  /** Broadcast a notification to all channels */
48
53
  async broadcastNotify(text, urgent) {
49
- await Promise.all(Array.from(this.channels.values()).map(channel => channel.sendNotification(text, urgent).catch(err => logger.warn(`${channel.name} notify failed:`, err))));
54
+ await Promise.all(Array.from(this.channels.values()).map((channel) => channel
55
+ .sendNotification(text, urgent)
56
+ .catch((err) => logger.warn(`${channel.name} notify failed:`, err))));
57
+ }
58
+ /**
59
+ * Broadcast a media attachment to every channel that supports it. Used by
60
+ * the pending-message dispatcher to deliver media queued by
61
+ * `beecork_send_media` and the `beecork_generate_*` tools — without this,
62
+ * those tools queue a row whose JSON envelope is never rendered.
63
+ *
64
+ * Channels that don't implement broadcastMedia get a sendNotification
65
+ * fallback so the user at least learns a file was generated.
66
+ */
67
+ async broadcastMedia(media) {
68
+ await Promise.all(Array.from(this.channels.values()).map((channel) => {
69
+ if (channel.broadcastMedia) {
70
+ return channel
71
+ .broadcastMedia(media)
72
+ .catch((err) => logger.warn(`${channel.name} broadcastMedia failed:`, err));
73
+ }
74
+ const fallback = `📎 Media generated: ${media.filePath}${media.caption ? `\n${media.caption}` : ''}`;
75
+ return channel
76
+ .sendNotification(fallback)
77
+ .catch((err) => logger.warn(`${channel.name} media-fallback notify failed:`, err));
78
+ }));
50
79
  }
51
80
  }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Send a tab-prefixed, chunked, retried response through any channel.
3
+ *
4
+ * Each channel just provides:
5
+ * - sendChunk(text): the platform-specific send call for one chunk
6
+ * - maxLength: the platform's per-message limit
7
+ * - retryLabel: label for logger (e.g. "telegram-send")
8
+ *
9
+ * Replaces near-identical per-channel implementations that had already
10
+ * drifted in subtle ways (different prefix gates, different retry envelopes).
11
+ */
12
+ export declare function sendChunkedResponse(opts: {
13
+ text: string;
14
+ tabName?: string;
15
+ maxLength: number;
16
+ retryLabel: string;
17
+ retryDelays?: number[];
18
+ sendChunk: (chunk: string) => Promise<unknown>;
19
+ }): Promise<void>;
@@ -0,0 +1,21 @@
1
+ import { chunkText, formatTabbedResponse } from '../util/text.js';
2
+ import { retryWithBackoff } from '../util/retry.js';
3
+ /**
4
+ * Send a tab-prefixed, chunked, retried response through any channel.
5
+ *
6
+ * Each channel just provides:
7
+ * - sendChunk(text): the platform-specific send call for one chunk
8
+ * - maxLength: the platform's per-message limit
9
+ * - retryLabel: label for logger (e.g. "telegram-send")
10
+ *
11
+ * Replaces near-identical per-channel implementations that had already
12
+ * drifted in subtle ways (different prefix gates, different retry envelopes).
13
+ */
14
+ export async function sendChunkedResponse(opts) {
15
+ const full = formatTabbedResponse(opts.text, opts.tabName);
16
+ const chunks = chunkText(full, opts.maxLength);
17
+ const delays = opts.retryDelays ?? [1000, 5000, 15000];
18
+ for (const chunk of chunks) {
19
+ await retryWithBackoff(() => opts.sendChunk(chunk), delays, opts.retryLabel);
20
+ }
21
+ }
@@ -1,38 +1,45 @@
1
- import type { Channel, ChannelContext, InboundMessageHandler, SendOptions } from './types.js';
2
- /** Format tab status for Telegram display */
3
- export declare function formatTabStatus(tabs: Array<{
4
- name: string;
5
- status: string;
6
- lastActivityAt: string;
7
- }>): string;
1
+ import type { Channel, ChannelContext, MediaAttachment, SendOptions } from './types.js';
8
2
  export declare class TelegramChannel implements Channel {
9
3
  readonly id = "telegram";
10
4
  readonly name = "Telegram";
11
5
  readonly maxMessageLength = 4096;
12
- readonly supportsStreaming = true;
13
- readonly supportsMedia = true;
14
6
  private bot;
15
7
  private ctx;
16
8
  private activeChatIds;
17
- private sttProvider;
18
- private ttsProvider;
9
+ private allowedUserIdSet;
10
+ private voice;
19
11
  private botUserId;
20
12
  private botUsername;
21
13
  private mutedGroups;
22
14
  private welcomeSent;
23
- private sttWarmedUp;
15
+ private pollingErrorTimes;
16
+ private pollingDegradedNotified;
24
17
  constructor(ctx: ChannelContext);
25
18
  start(): Promise<void>;
19
+ /**
20
+ * Track polling errors over a 60s rolling window. If we see 5+ errors in 60s
21
+ * we surface "polling degraded" exactly once via the notify callback so the
22
+ * user knows Telegram is broken instead of silently failing.
23
+ */
24
+ private recordPollingError;
26
25
  stop(): void;
27
- sendMessage(peerId: string, text: string, options?: SendOptions): Promise<void>;
26
+ sendMessage(peerId: string, text: string, _options?: SendOptions): Promise<void>;
28
27
  sendNotification(message: string, _urgent?: boolean): Promise<void>;
28
+ /**
29
+ * Send a media file to every recipient the bot would notify. Used by the
30
+ * pending-message dispatcher to deliver media queued by MCP tools.
31
+ * Routes by attachment type to the right Telegram primitive (sendVoice for
32
+ * voice messages, sendPhoto for images, sendVideo for video, sendDocument
33
+ * for everything else).
34
+ */
35
+ broadcastMedia(media: MediaAttachment): Promise<void>;
29
36
  setTyping(peerId: string, active: boolean): Promise<void>;
30
- onMessage(_handler: InboundMessageHandler): void;
31
37
  private setupHandlers;
32
38
  private handleCommand;
33
39
  private handleMessage;
34
40
  private sendResponse;
35
41
  private sendWithRetry;
42
+ private sendWithRetryRaw;
36
43
  private downloadTelegramFile;
37
44
  private isAllowed;
38
45
  private isAdmin;