beecork 1.7.1 → 2.0.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 (253) hide show
  1. package/README.md +52 -111
  2. package/dist/index.js +1394 -581
  3. package/package.json +26 -54
  4. package/dist/capabilities/index.d.ts +0 -3
  5. package/dist/capabilities/index.js +0 -2
  6. package/dist/capabilities/manager.d.ts +0 -13
  7. package/dist/capabilities/manager.js +0 -115
  8. package/dist/capabilities/packs.d.ts +0 -2
  9. package/dist/capabilities/packs.js +0 -54
  10. package/dist/capabilities/types.d.ts +0 -21
  11. package/dist/capabilities/types.js +0 -1
  12. package/dist/channels/admin.d.ts +0 -10
  13. package/dist/channels/admin.js +0 -20
  14. package/dist/channels/command-handler.d.ts +0 -19
  15. package/dist/channels/command-handler.js +0 -235
  16. package/dist/channels/discord.d.ts +0 -17
  17. package/dist/channels/discord.js +0 -266
  18. package/dist/channels/index.d.ts +0 -7
  19. package/dist/channels/index.js +0 -6
  20. package/dist/channels/loader.d.ts +0 -7
  21. package/dist/channels/loader.js +0 -87
  22. package/dist/channels/pipeline.d.ts +0 -50
  23. package/dist/channels/pipeline.js +0 -92
  24. package/dist/channels/registry.d.ts +0 -36
  25. package/dist/channels/registry.js +0 -80
  26. package/dist/channels/send-helpers.d.ts +0 -19
  27. package/dist/channels/send-helpers.js +0 -21
  28. package/dist/channels/telegram.d.ts +0 -47
  29. package/dist/channels/telegram.js +0 -649
  30. package/dist/channels/types.d.ts +0 -41
  31. package/dist/channels/types.js +0 -1
  32. package/dist/channels/voice-state.d.ts +0 -29
  33. package/dist/channels/voice-state.js +0 -45
  34. package/dist/channels/webhook.d.ts +0 -16
  35. package/dist/channels/webhook.js +0 -222
  36. package/dist/channels/whatsapp.d.ts +0 -28
  37. package/dist/channels/whatsapp.js +0 -323
  38. package/dist/cli/capabilities.d.ts +0 -3
  39. package/dist/cli/capabilities.js +0 -72
  40. package/dist/cli/channel.d.ts +0 -3
  41. package/dist/cli/channel.js +0 -202
  42. package/dist/cli/commands.d.ts +0 -15
  43. package/dist/cli/commands.js +0 -279
  44. package/dist/cli/doctor.d.ts +0 -1
  45. package/dist/cli/doctor.js +0 -208
  46. package/dist/cli/handoff.d.ts +0 -8
  47. package/dist/cli/handoff.js +0 -33
  48. package/dist/cli/helpers.d.ts +0 -2
  49. package/dist/cli/helpers.js +0 -21
  50. package/dist/cli/mcp.d.ts +0 -3
  51. package/dist/cli/mcp.js +0 -47
  52. package/dist/cli/media.d.ts +0 -2
  53. package/dist/cli/media.js +0 -125
  54. package/dist/cli/setup.d.ts +0 -1
  55. package/dist/cli/setup.js +0 -239
  56. package/dist/cli/store.d.ts +0 -3
  57. package/dist/cli/store.js +0 -108
  58. package/dist/config.d.ts +0 -11
  59. package/dist/config.js +0 -115
  60. package/dist/cron/scheduler.d.ts +0 -22
  61. package/dist/cron/scheduler.js +0 -220
  62. package/dist/cron/store.d.ts +0 -12
  63. package/dist/cron/store.js +0 -83
  64. package/dist/daemon.d.ts +0 -1
  65. package/dist/daemon.js +0 -345
  66. package/dist/dashboard/html.d.ts +0 -1
  67. package/dist/dashboard/html.js +0 -849
  68. package/dist/dashboard/routes.d.ts +0 -17
  69. package/dist/dashboard/routes.js +0 -623
  70. package/dist/dashboard/server.d.ts +0 -1
  71. package/dist/dashboard/server.js +0 -104
  72. package/dist/db/connection.d.ts +0 -29
  73. package/dist/db/connection.js +0 -37
  74. package/dist/db/index.d.ts +0 -14
  75. package/dist/db/index.js +0 -127
  76. package/dist/db/migrations.d.ts +0 -2
  77. package/dist/db/migrations.js +0 -384
  78. package/dist/delegation/manager.d.ts +0 -18
  79. package/dist/delegation/manager.js +0 -89
  80. package/dist/index.d.ts +0 -2
  81. package/dist/knowledge/index.d.ts +0 -2
  82. package/dist/knowledge/index.js +0 -1
  83. package/dist/knowledge/manager.d.ts +0 -17
  84. package/dist/knowledge/manager.js +0 -160
  85. package/dist/knowledge/types.d.ts +0 -7
  86. package/dist/knowledge/types.js +0 -1
  87. package/dist/machines/forwarder.d.ts +0 -7
  88. package/dist/machines/forwarder.js +0 -35
  89. package/dist/machines/index.d.ts +0 -1
  90. package/dist/machines/index.js +0 -1
  91. package/dist/machines/registry.d.ts +0 -15
  92. package/dist/machines/registry.js +0 -46
  93. package/dist/mcp/handlers.d.ts +0 -37
  94. package/dist/mcp/handlers.js +0 -520
  95. package/dist/mcp/server.d.ts +0 -2
  96. package/dist/mcp/server.js +0 -86
  97. package/dist/mcp/tool-definitions.d.ts +0 -1225
  98. package/dist/mcp/tool-definitions.js +0 -412
  99. package/dist/mcp/validate.d.ts +0 -23
  100. package/dist/mcp/validate.js +0 -65
  101. package/dist/media/factory.d.ts +0 -6
  102. package/dist/media/factory.js +0 -36
  103. package/dist/media/generators/dall-e.d.ts +0 -10
  104. package/dist/media/generators/dall-e.js +0 -41
  105. package/dist/media/generators/elevenlabs-music.d.ts +0 -9
  106. package/dist/media/generators/elevenlabs-music.js +0 -34
  107. package/dist/media/generators/elevenlabs-sfx.d.ts +0 -9
  108. package/dist/media/generators/elevenlabs-sfx.js +0 -33
  109. package/dist/media/generators/kling.d.ts +0 -9
  110. package/dist/media/generators/kling.js +0 -47
  111. package/dist/media/generators/lyria.d.ts +0 -10
  112. package/dist/media/generators/lyria.js +0 -41
  113. package/dist/media/generators/nano-banana.d.ts +0 -10
  114. package/dist/media/generators/nano-banana.js +0 -45
  115. package/dist/media/generators/poll-util.d.ts +0 -12
  116. package/dist/media/generators/poll-util.js +0 -22
  117. package/dist/media/generators/recraft.d.ts +0 -9
  118. package/dist/media/generators/recraft.js +0 -52
  119. package/dist/media/generators/runway.d.ts +0 -9
  120. package/dist/media/generators/runway.js +0 -49
  121. package/dist/media/generators/stable-diffusion.d.ts +0 -9
  122. package/dist/media/generators/stable-diffusion.js +0 -35
  123. package/dist/media/generators/veo.d.ts +0 -9
  124. package/dist/media/generators/veo.js +0 -32
  125. package/dist/media/index.d.ts +0 -6
  126. package/dist/media/index.js +0 -8
  127. package/dist/media/loader.d.ts +0 -8
  128. package/dist/media/loader.js +0 -46
  129. package/dist/media/store.d.ts +0 -17
  130. package/dist/media/store.js +0 -69
  131. package/dist/media/types.d.ts +0 -43
  132. package/dist/media/types.js +0 -1
  133. package/dist/memory/extractor.d.ts +0 -5
  134. package/dist/memory/extractor.js +0 -157
  135. package/dist/notifications/index.d.ts +0 -4
  136. package/dist/notifications/index.js +0 -21
  137. package/dist/notifications/ntfy.d.ts +0 -9
  138. package/dist/notifications/ntfy.js +0 -25
  139. package/dist/notifications/pushover.d.ts +0 -9
  140. package/dist/notifications/pushover.js +0 -26
  141. package/dist/notifications/types.d.ts +0 -5
  142. package/dist/notifications/types.js +0 -1
  143. package/dist/notifications/webhook-provider.d.ts +0 -9
  144. package/dist/notifications/webhook-provider.js +0 -24
  145. package/dist/observability/analytics.d.ts +0 -30
  146. package/dist/observability/analytics.js +0 -118
  147. package/dist/pipe/anthropic-client.d.ts +0 -9
  148. package/dist/pipe/anthropic-client.js +0 -50
  149. package/dist/pipe/brain.d.ts +0 -16
  150. package/dist/pipe/brain.js +0 -89
  151. package/dist/pipe/memory-store.d.ts +0 -8
  152. package/dist/pipe/memory-store.js +0 -39
  153. package/dist/pipe/project-scanner.d.ts +0 -6
  154. package/dist/pipe/project-scanner.js +0 -26
  155. package/dist/pipe/types.d.ts +0 -34
  156. package/dist/pipe/types.js +0 -1
  157. package/dist/projects/index.d.ts +0 -4
  158. package/dist/projects/index.js +0 -2
  159. package/dist/projects/manager.d.ts +0 -13
  160. package/dist/projects/manager.js +0 -166
  161. package/dist/projects/router.d.ts +0 -17
  162. package/dist/projects/router.js +0 -246
  163. package/dist/projects/types.d.ts +0 -19
  164. package/dist/projects/types.js +0 -1
  165. package/dist/service/install.d.ts +0 -4
  166. package/dist/service/install.js +0 -146
  167. package/dist/service/templates.d.ts +0 -4
  168. package/dist/service/templates.js +0 -52
  169. package/dist/service/windows.d.ts +0 -8
  170. package/dist/service/windows.js +0 -59
  171. package/dist/session/budget-guard.d.ts +0 -20
  172. package/dist/session/budget-guard.js +0 -31
  173. package/dist/session/circuit-breaker.d.ts +0 -19
  174. package/dist/session/circuit-breaker.js +0 -91
  175. package/dist/session/context-compactor.d.ts +0 -32
  176. package/dist/session/context-compactor.js +0 -45
  177. package/dist/session/context-monitor.d.ts +0 -15
  178. package/dist/session/context-monitor.js +0 -44
  179. package/dist/session/handoff.d.ts +0 -21
  180. package/dist/session/handoff.js +0 -50
  181. package/dist/session/manager.d.ts +0 -60
  182. package/dist/session/manager.js +0 -416
  183. package/dist/session/memory-store.d.ts +0 -29
  184. package/dist/session/memory-store.js +0 -45
  185. package/dist/session/message-queue.d.ts +0 -28
  186. package/dist/session/message-queue.js +0 -52
  187. package/dist/session/pending-dispatcher.d.ts +0 -31
  188. package/dist/session/pending-dispatcher.js +0 -120
  189. package/dist/session/pending-store.d.ts +0 -60
  190. package/dist/session/pending-store.js +0 -118
  191. package/dist/session/stale-session.d.ts +0 -31
  192. package/dist/session/stale-session.js +0 -45
  193. package/dist/session/subprocess.d.ts +0 -32
  194. package/dist/session/subprocess.js +0 -230
  195. package/dist/session/tab-store.d.ts +0 -28
  196. package/dist/session/tab-store.js +0 -78
  197. package/dist/session/tool-classifier.d.ts +0 -4
  198. package/dist/session/tool-classifier.js +0 -56
  199. package/dist/tasks/scheduler.d.ts +0 -45
  200. package/dist/tasks/scheduler.js +0 -329
  201. package/dist/tasks/store.d.ts +0 -11
  202. package/dist/tasks/store.js +0 -103
  203. package/dist/timeline/index.d.ts +0 -3
  204. package/dist/timeline/index.js +0 -2
  205. package/dist/timeline/logger.d.ts +0 -8
  206. package/dist/timeline/logger.js +0 -11
  207. package/dist/timeline/query.d.ts +0 -11
  208. package/dist/timeline/query.js +0 -67
  209. package/dist/timeline/types.d.ts +0 -12
  210. package/dist/timeline/types.js +0 -1
  211. package/dist/types.d.ts +0 -225
  212. package/dist/types.js +0 -3
  213. package/dist/users/index.d.ts +0 -2
  214. package/dist/users/index.js +0 -1
  215. package/dist/users/service.d.ts +0 -17
  216. package/dist/users/service.js +0 -46
  217. package/dist/util/auto-heal.d.ts +0 -43
  218. package/dist/util/auto-heal.js +0 -107
  219. package/dist/util/install-info.d.ts +0 -37
  220. package/dist/util/install-info.js +0 -80
  221. package/dist/util/logger.d.ts +0 -18
  222. package/dist/util/logger.js +0 -118
  223. package/dist/util/paths.d.ts +0 -13
  224. package/dist/util/paths.js +0 -55
  225. package/dist/util/platform.d.ts +0 -2
  226. package/dist/util/platform.js +0 -11
  227. package/dist/util/progress.d.ts +0 -34
  228. package/dist/util/progress.js +0 -133
  229. package/dist/util/rate-limiter.d.ts +0 -14
  230. package/dist/util/rate-limiter.js +0 -50
  231. package/dist/util/retry.d.ts +0 -2
  232. package/dist/util/retry.js +0 -18
  233. package/dist/util/text.d.ts +0 -32
  234. package/dist/util/text.js +0 -101
  235. package/dist/version.d.ts +0 -1
  236. package/dist/version.js +0 -4
  237. package/dist/voice/index.d.ts +0 -17
  238. package/dist/voice/index.js +0 -42
  239. package/dist/voice/stt.d.ts +0 -16
  240. package/dist/voice/stt.js +0 -57
  241. package/dist/voice/tts.d.ts +0 -23
  242. package/dist/voice/tts.js +0 -84
  243. package/dist/watchers/evaluator.d.ts +0 -5
  244. package/dist/watchers/evaluator.js +0 -45
  245. package/dist/watchers/index.d.ts +0 -4
  246. package/dist/watchers/index.js +0 -3
  247. package/dist/watchers/scheduler.d.ts +0 -20
  248. package/dist/watchers/scheduler.js +0 -183
  249. package/dist/watchers/store.d.ts +0 -10
  250. package/dist/watchers/store.js +0 -56
  251. package/dist/watchers/types.d.ts +0 -15
  252. package/dist/watchers/types.js +0 -1
  253. package/scripts/postinstall.mjs +0 -50
@@ -1,52 +0,0 @@
1
- /**
2
- * Per-tab FIFO queue for messages waiting on a busy subprocess.
3
- *
4
- * Extracted from TabManager so the queue's "max size, enqueue, dequeue,
5
- * peek-and-mutate-next, clear-on-stop" semantics are self-contained and
6
- * unit-testable without spinning up a real subprocess.
7
- */
8
- export const MAX_QUEUE_SIZE = 10;
9
- export class MessageQueue {
10
- queues = new Map();
11
- /** Attempt to enqueue. Returns false if the per-tab queue is full. */
12
- enqueue(tabName, msg) {
13
- let queue = this.queues.get(tabName);
14
- if (!queue) {
15
- queue = [];
16
- this.queues.set(tabName, queue);
17
- }
18
- if (queue.length >= MAX_QUEUE_SIZE)
19
- return false;
20
- queue.push(msg);
21
- return true;
22
- }
23
- /** Remove and return the next message. Drops the per-tab entry when empty. */
24
- dequeue(tabName) {
25
- const queue = this.queues.get(tabName);
26
- if (!queue || queue.length === 0)
27
- return undefined;
28
- const next = queue.shift();
29
- if (queue.length === 0)
30
- this.queues.delete(tabName);
31
- return next;
32
- }
33
- /** Peek at the next queued message without removing it (used to inject loop warning prefix). */
34
- peek(tabName) {
35
- return this.queues.get(tabName)?.[0];
36
- }
37
- size(tabName) {
38
- return this.queues.get(tabName)?.length ?? 0;
39
- }
40
- /** Drain the queue for a tab, returning the dropped messages so callers can reject them. */
41
- clear(tabName) {
42
- const queue = this.queues.get(tabName) ?? [];
43
- this.queues.delete(tabName);
44
- return queue;
45
- }
46
- /** Drain every queue. Returns the dropped messages grouped by tab. */
47
- clearAll() {
48
- const all = Array.from(this.queues.values());
49
- this.queues.clear();
50
- return all;
51
- }
52
- }
@@ -1,31 +0,0 @@
1
- /**
2
- * Polls `pending_messages` and dispatches each row by type.
3
- *
4
- * Replaces the inline `processPendingMessages` block that previously lived in
5
- * TabManager. Two functional changes from that code:
6
- *
7
- * 1. Claim-before-dispatch (status='processing' set atomically before send).
8
- * The original poll-every-5s code re-picked the same row on every tick
9
- * while its dispatch was in flight — long Claude runs could re-fire the
10
- * same prompt many times.
11
- *
12
- * 2. Branch on type. The original consumer only recognized 'notification' and
13
- * fell through to "send as plain user prompt" for everything else, which
14
- * meant `type='media'` rows were handed to Claude as raw JSON and never
15
- * actually delivered to channels.
16
- */
17
- import type { TabManager } from './manager.js';
18
- import type { ChannelRegistry } from '../channels/registry.js';
19
- export type NotifyFn = ((text: string) => Promise<void>) | null;
20
- export declare class PendingMessageDispatcher {
21
- private tabManager;
22
- private channels;
23
- private onNotify;
24
- private pollCount;
25
- constructor(tabManager: TabManager, channels: ChannelRegistry, onNotify: NotifyFn);
26
- /** Reset any rows stuck in 'processing' from a previous daemon run. */
27
- recoverOnStart(): void;
28
- /** Called on every daemon poll tick. */
29
- tick(): void;
30
- private dispatch;
31
- }
@@ -1,120 +0,0 @@
1
- /**
2
- * Polls `pending_messages` and dispatches each row by type.
3
- *
4
- * Replaces the inline `processPendingMessages` block that previously lived in
5
- * TabManager. Two functional changes from that code:
6
- *
7
- * 1. Claim-before-dispatch (status='processing' set atomically before send).
8
- * The original poll-every-5s code re-picked the same row on every tick
9
- * while its dispatch was in flight — long Claude runs could re-fire the
10
- * same prompt many times.
11
- *
12
- * 2. Branch on type. The original consumer only recognized 'notification' and
13
- * fell through to "send as plain user prompt" for everything else, which
14
- * meant `type='media'` rows were handed to Claude as raw JSON and never
15
- * actually delivered to channels.
16
- */
17
- import { getDb } from '../db/index.js';
18
- import { logger } from '../util/logger.js';
19
- import { PendingMessageStore } from './pending-store.js';
20
- const POLL_LIMIT = 50;
21
- const CLEANUP_EVERY_N_TICKS = 100;
22
- export class PendingMessageDispatcher {
23
- tabManager;
24
- channels;
25
- onNotify;
26
- pollCount = 0;
27
- constructor(tabManager, channels, onNotify) {
28
- this.tabManager = tabManager;
29
- this.channels = channels;
30
- this.onNotify = onNotify;
31
- }
32
- /** Reset any rows stuck in 'processing' from a previous daemon run. */
33
- recoverOnStart() {
34
- PendingMessageStore.recoverProcessing();
35
- }
36
- /** Called on every daemon poll tick. */
37
- tick() {
38
- const db = getDb();
39
- this.pollCount++;
40
- if (this.pollCount % CLEANUP_EVERY_N_TICKS === 0) {
41
- db.prepare("DELETE FROM pending_messages WHERE (status = 'done' OR status = 'failed') AND created_at < datetime('now', '-1 day')").run();
42
- }
43
- const claimed = PendingMessageStore.claimBatch(POLL_LIMIT, db);
44
- for (const row of claimed) {
45
- // Fire-and-forget per row — the row is already marked 'processing' so
46
- // the next tick won't double-dispatch. The .finally() handles the
47
- // terminal transition.
48
- void this.dispatch(row);
49
- }
50
- }
51
- async dispatch(row) {
52
- try {
53
- switch (row.type) {
54
- case 'notification':
55
- await this.onNotify?.(row.message);
56
- break;
57
- case 'media': {
58
- const payload = PendingMessageStore.parseMediaPayload(row.message);
59
- if (!payload) {
60
- logger.warn(`Pending media row ${row.id} has malformed payload, skipping`);
61
- break;
62
- }
63
- const attachment = inferAttachmentType(payload.filePath, payload.caption);
64
- await this.channels.broadcastMedia(attachment);
65
- break;
66
- }
67
- case 'user':
68
- case 'delegation':
69
- case 'delegation_result':
70
- case 'replay': {
71
- if (!row.tabName) {
72
- logger.warn(`Pending row ${row.id} (type=${row.type}) has no tab_name, skipping`);
73
- break;
74
- }
75
- await this.tabManager.sendMessage(row.tabName, row.message);
76
- break;
77
- }
78
- default: {
79
- // Unknown discriminant — log and bail rather than fall through to
80
- // "send as a plain prompt" (which is how H5 ended up shipping).
81
- logger.warn(`Pending row ${row.id} has unknown type "${row.type}", marking failed`);
82
- PendingMessageStore.markFailed(row.id);
83
- return;
84
- }
85
- }
86
- PendingMessageStore.markDone(row.id);
87
- }
88
- catch (err) {
89
- logger.error(`Failed to process pending message ${row.id} (type=${row.type}):`, err);
90
- await this.onNotify?.(`Pending message ${row.type === 'notification' ? '' : `for tab "${row.tabName}"`} failed: ${err instanceof Error ? err.message : String(err)}`).catch(() => { });
91
- PendingMessageStore.markFailed(row.id);
92
- }
93
- }
94
- }
95
- /**
96
- * Heuristic: derive a MediaAttachment from a file path so the channel's
97
- * broadcastMedia knows whether to send as voice / image / video / document.
98
- */
99
- function inferAttachmentType(filePath, caption) {
100
- const ext = filePath.split('.').pop()?.toLowerCase() ?? '';
101
- let type = 'document';
102
- let mimeType = 'application/octet-stream';
103
- if (['png', 'jpg', 'jpeg', 'gif', 'webp'].includes(ext)) {
104
- type = 'image';
105
- mimeType = `image/${ext === 'jpg' ? 'jpeg' : ext}`;
106
- }
107
- else if (['mp4', 'mov', 'webm', 'mkv'].includes(ext)) {
108
- type = 'video';
109
- mimeType = `video/${ext === 'mov' ? 'quicktime' : ext}`;
110
- }
111
- else if (['ogg', 'opus', 'oga'].includes(ext)) {
112
- type = 'voice';
113
- mimeType = 'audio/ogg';
114
- }
115
- else if (['mp3', 'm4a', 'wav', 'flac', 'aac'].includes(ext)) {
116
- type = 'audio';
117
- mimeType = `audio/${ext}`;
118
- }
119
- return { type, mimeType, filePath, caption };
120
- }
@@ -1,60 +0,0 @@
1
- /**
2
- * Inter-process IPC store for the `pending_messages` table.
3
- *
4
- * Beecork uses this table as a shared queue between writers (MCP handlers,
5
- * dashboard routes, watchers, delegations) and the daemon's poll loop. Before
6
- * this module the writes were inline `INSERT INTO pending_messages` calls
7
- * scattered across nine sites; the consumer in TabManager only branched on
8
- * type='notification' which meant every other type fell through to "send as
9
- * a plain user prompt" — including `type='media'` rows whose JSON envelope
10
- * was being handed to Claude as text instead of being rendered.
11
- *
12
- * This store owns:
13
- * - the typed envelope (`PendingMessageType` union, no more magic strings)
14
- * - 3-state status (`pending` → `processing` → `done|failed`) so the 5s
15
- * poll loop doesn't re-dispatch long-running rows
16
- * - the `_notify` sentinel removal (notifications now use type alone,
17
- * tab_name is nullable)
18
- * - claim/recover semantics so a daemon crash mid-dispatch resets in-flight
19
- * rows back to pending after a 30-minute timeout
20
- */
21
- import type Database from 'better-sqlite3';
22
- export type PendingMessageType = 'user' | 'notification' | 'media' | 'delegation' | 'delegation_result' | 'replay';
23
- export interface PendingRow {
24
- id: number;
25
- tabName: string | null;
26
- message: string;
27
- type: PendingMessageType;
28
- status: 'pending' | 'processing' | 'done' | 'failed';
29
- createdAt: string;
30
- }
31
- interface MediaPayload {
32
- type: 'media';
33
- filePath: string;
34
- caption?: string;
35
- }
36
- export declare const PendingMessageStore: {
37
- enqueueUser(tabName: string, message: string, db?: Database.Database): void;
38
- enqueueNotification(message: string, db?: Database.Database): void;
39
- enqueueMedia(tabName: string, payload: MediaPayload, db?: Database.Database): void;
40
- enqueueDelegation(tabName: string, message: string, db?: Database.Database): void;
41
- enqueueDelegationResult(tabName: string, message: string, db?: Database.Database): void;
42
- enqueueReplay(tabName: string, message: string, db?: Database.Database): void;
43
- /**
44
- * Claim up to `limit` pending rows atomically. The rows are flipped to
45
- * status='processing' inside a transaction so a second poll tick can't pick
46
- * them up. Returns the claimed rows.
47
- */
48
- claimBatch(limit: number, db?: Database.Database): PendingRow[];
49
- markDone(id: number, db?: Database.Database): void;
50
- markFailed(id: number, db?: Database.Database): void;
51
- /**
52
- * Reset stuck `processing` rows back to `pending`. Run at daemon startup so
53
- * a crash mid-dispatch doesn't leave rows permanently in-flight. The 30min
54
- * window is well beyond the 30min subprocess runtime cap.
55
- */
56
- recoverProcessing(db?: Database.Database): number;
57
- /** Parse a media row's message JSON. Returns null on malformed JSON. */
58
- parseMediaPayload(message: string): MediaPayload | null;
59
- };
60
- export {};
@@ -1,118 +0,0 @@
1
- /**
2
- * Inter-process IPC store for the `pending_messages` table.
3
- *
4
- * Beecork uses this table as a shared queue between writers (MCP handlers,
5
- * dashboard routes, watchers, delegations) and the daemon's poll loop. Before
6
- * this module the writes were inline `INSERT INTO pending_messages` calls
7
- * scattered across nine sites; the consumer in TabManager only branched on
8
- * type='notification' which meant every other type fell through to "send as
9
- * a plain user prompt" — including `type='media'` rows whose JSON envelope
10
- * was being handed to Claude as text instead of being rendered.
11
- *
12
- * This store owns:
13
- * - the typed envelope (`PendingMessageType` union, no more magic strings)
14
- * - 3-state status (`pending` → `processing` → `done|failed`) so the 5s
15
- * poll loop doesn't re-dispatch long-running rows
16
- * - the `_notify` sentinel removal (notifications now use type alone,
17
- * tab_name is nullable)
18
- * - claim/recover semantics so a daemon crash mid-dispatch resets in-flight
19
- * rows back to pending after a 30-minute timeout
20
- */
21
- import { getDb } from '../db/index.js';
22
- import { logger } from '../util/logger.js';
23
- function rowToPending(r) {
24
- return {
25
- id: r.id,
26
- tabName: r.tab_name,
27
- message: r.message,
28
- type: r.type || 'user',
29
- status: r.status || 'pending',
30
- createdAt: r.created_at,
31
- };
32
- }
33
- const STALE_PROCESSING_MS = 30 * 60 * 1000; // 30 minutes
34
- // Private INSERT helper — all five enqueue* methods were the same INSERT with
35
- // different literals. Notification rows use '' for tab_name since the consumer
36
- // branches on type rather than reading the tab.
37
- function insertPending(db, tabName, message, type) {
38
- db.prepare('INSERT INTO pending_messages (tab_name, message, type, status) VALUES (?, ?, ?, ?)').run(tabName, message, type, 'pending');
39
- }
40
- export const PendingMessageStore = {
41
- enqueueUser(tabName, message, db = getDb()) {
42
- insertPending(db, tabName, message, 'user');
43
- },
44
- enqueueNotification(message, db = getDb()) {
45
- insertPending(db, '', message, 'notification');
46
- },
47
- enqueueMedia(tabName, payload, db = getDb()) {
48
- insertPending(db, tabName, JSON.stringify(payload), 'media');
49
- },
50
- enqueueDelegation(tabName, message, db = getDb()) {
51
- insertPending(db, tabName, message, 'delegation');
52
- },
53
- enqueueDelegationResult(tabName, message, db = getDb()) {
54
- insertPending(db, tabName, message, 'delegation_result');
55
- },
56
- enqueueReplay(tabName, message, db = getDb()) {
57
- insertPending(db, tabName, message, 'replay');
58
- },
59
- /**
60
- * Claim up to `limit` pending rows atomically. The rows are flipped to
61
- * status='processing' inside a transaction so a second poll tick can't pick
62
- * them up. Returns the claimed rows.
63
- */
64
- claimBatch(limit, db = getDb()) {
65
- const claim = db.transaction((max) => {
66
- const rows = db
67
- .prepare("SELECT id, tab_name, message, type, status, created_at FROM pending_messages WHERE status = 'pending' ORDER BY created_at ASC LIMIT ?")
68
- .all(max);
69
- if (rows.length === 0)
70
- return [];
71
- const stmt = db.prepare("UPDATE pending_messages SET status = 'processing' WHERE id = ? AND status = 'pending'");
72
- const claimed = [];
73
- for (const r of rows) {
74
- const result = stmt.run(r.id);
75
- if (result.changes > 0)
76
- claimed.push(rowToPending(r));
77
- }
78
- return claimed;
79
- });
80
- return claim(limit);
81
- },
82
- markDone(id, db = getDb()) {
83
- db.prepare("UPDATE pending_messages SET status = 'done', processed = 1 WHERE id = ?").run(id);
84
- },
85
- markFailed(id, db = getDb()) {
86
- // 'failed' rows are kept for diagnostics until the periodic cleanup sweeps
87
- // them. They are NOT re-dispatched — preserves the original "no infinite
88
- // retries" policy while keeping the row visible for postmortem queries.
89
- db.prepare("UPDATE pending_messages SET status = 'failed', processed = 1 WHERE id = ?").run(id);
90
- },
91
- /**
92
- * Reset stuck `processing` rows back to `pending`. Run at daemon startup so
93
- * a crash mid-dispatch doesn't leave rows permanently in-flight. The 30min
94
- * window is well beyond the 30min subprocess runtime cap.
95
- */
96
- recoverProcessing(db = getDb()) {
97
- const result = db
98
- .prepare(`UPDATE pending_messages SET status = 'pending'
99
- WHERE status = 'processing' AND created_at < datetime('now', '-' || ? || ' seconds')`)
100
- .run(Math.floor(STALE_PROCESSING_MS / 1000));
101
- if (result.changes > 0) {
102
- logger.warn(`Recovered ${result.changes} stuck pending_messages rows from 'processing' state`);
103
- }
104
- return result.changes;
105
- },
106
- /** Parse a media row's message JSON. Returns null on malformed JSON. */
107
- parseMediaPayload(message) {
108
- try {
109
- const parsed = JSON.parse(message);
110
- if (parsed?.type === 'media' && typeof parsed.filePath === 'string')
111
- return parsed;
112
- return null;
113
- }
114
- catch {
115
- return null;
116
- }
117
- },
118
- };
@@ -1,31 +0,0 @@
1
- /**
2
- * Detection + recovery for "Claude Code session not found / expired" errors.
3
- *
4
- * Extracted from TabManager.executeMessage so the two responsibilities
5
- * (recognize the symptom, build the recovery prompt) live in one focused
6
- * module instead of tangled inline with the subprocess callbacks.
7
- */
8
- import type Database from 'better-sqlite3';
9
- import type { StreamResult } from '../types.js';
10
- import type { SendResult } from './manager.js';
11
- export interface StaleSessionRecovery {
12
- newSessionId: string;
13
- /** A prompt that wraps the original enrichedPrompt with the last few messages
14
- * for context, since Claude Code lost the session and won't have any. */
15
- contextPrompt: string;
16
- }
17
- export declare const StaleSessionDetector: {
18
- /**
19
- * Was this result an "I can't resume that session" failure?
20
- *
21
- * Covers both the legacy text-based error and the modern
22
- * `error_during_execution` event shape.
23
- */
24
- isStale(result: SendResult, resultEvent: StreamResult | null, shouldResume: boolean, retryDepth: number): boolean;
25
- /**
26
- * Build a recovery prompt seeded with the tab's last 5 messages as context,
27
- * and allocate a fresh session ID. Caller is responsible for updating the
28
- * tab's session_id in the DB before retrying.
29
- */
30
- buildRecovery(tabId: string, enrichedPrompt: string, db: Database.Database): StaleSessionRecovery;
31
- };
@@ -1,45 +0,0 @@
1
- /**
2
- * Detection + recovery for "Claude Code session not found / expired" errors.
3
- *
4
- * Extracted from TabManager.executeMessage so the two responsibilities
5
- * (recognize the symptom, build the recovery prompt) live in one focused
6
- * module instead of tangled inline with the subprocess callbacks.
7
- */
8
- import { v4 as uuidv4 } from 'uuid';
9
- export const StaleSessionDetector = {
10
- /**
11
- * Was this result an "I can't resume that session" failure?
12
- *
13
- * Covers both the legacy text-based error and the modern
14
- * `error_during_execution` event shape.
15
- */
16
- isStale(result, resultEvent, shouldResume, retryDepth) {
17
- if (!result.error || !shouldResume || retryDepth !== 0)
18
- return false;
19
- if (/session (not found|expired|invalid)/i.test(result.text))
20
- return true;
21
- if (resultEvent?.subtype === 'error_during_execution' &&
22
- resultEvent.errors?.some((e) => /no conversation found|session.*not found|session.*expired|session.*invalid/i.test(e))) {
23
- return true;
24
- }
25
- return false;
26
- },
27
- /**
28
- * Build a recovery prompt seeded with the tab's last 5 messages as context,
29
- * and allocate a fresh session ID. Caller is responsible for updating the
30
- * tab's session_id in the DB before retrying.
31
- */
32
- buildRecovery(tabId, enrichedPrompt, db) {
33
- const recentMsgs = db
34
- .prepare("SELECT role, content FROM messages WHERE tab_id = ? AND content != '' ORDER BY created_at DESC LIMIT 5")
35
- .all(tabId);
36
- const context = recentMsgs
37
- .reverse()
38
- .map((m) => `${m.role}: ${m.content.slice(0, 200)}`)
39
- .join('\n');
40
- const contextPrompt = context
41
- ? `[Previous conversation context:\n${context}\n]\n\n${enrichedPrompt}`
42
- : enrichedPrompt;
43
- return { newSessionId: uuidv4(), contextPrompt };
44
- },
45
- };
@@ -1,32 +0,0 @@
1
- import type { BeecorkConfig, StreamEvent } from '../types.js';
2
- /** Test-only — reset the existsSync cache between test runs that toggle the mock. */
3
- export declare function _resetMcpConfigExistsCacheForTests(): void;
4
- export interface SubprocessCallbacks {
5
- onEvent: (event: StreamEvent) => void;
6
- onExit: (code: number | null) => void;
7
- onError: (err: Error) => void;
8
- }
9
- export declare class ClaudeSubprocess {
10
- private tabName;
11
- private workingDir;
12
- private config;
13
- private tabSystemPrompt?;
14
- private proc;
15
- private buffer;
16
- private killTimer;
17
- private runtimeTimer;
18
- private startupTimer;
19
- /**
20
- * Set when the startup watchdog kills a subprocess that never emitted a
21
- * single event — distinguishes a transient network/connection stall from a
22
- * normal exit or a real wall-clock overrun, so the manager can retry once.
23
- */
24
- killReason: 'silent' | null;
25
- readonly sessionId: string;
26
- constructor(tabName: string, workingDir: string, config: BeecorkConfig, sessionId?: string, tabSystemPrompt?: string | null | undefined);
27
- send(prompt: string, callbacks: SubprocessCallbacks, resume?: boolean): Promise<void>;
28
- kill(): void;
29
- get isRunning(): boolean;
30
- get pid(): number | null;
31
- private buildArgs;
32
- }