@johpaz/hive 1.1.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 (156) hide show
  1. package/CONTRIBUTING.md +44 -0
  2. package/README.md +310 -0
  3. package/package.json +96 -0
  4. package/packages/cli/package.json +28 -0
  5. package/packages/cli/src/commands/agent-run.ts +168 -0
  6. package/packages/cli/src/commands/agents.ts +398 -0
  7. package/packages/cli/src/commands/chat.ts +142 -0
  8. package/packages/cli/src/commands/config.ts +50 -0
  9. package/packages/cli/src/commands/cron.ts +161 -0
  10. package/packages/cli/src/commands/dev.ts +95 -0
  11. package/packages/cli/src/commands/doctor.ts +133 -0
  12. package/packages/cli/src/commands/gateway.ts +443 -0
  13. package/packages/cli/src/commands/logs.ts +57 -0
  14. package/packages/cli/src/commands/mcp.ts +175 -0
  15. package/packages/cli/src/commands/message.ts +77 -0
  16. package/packages/cli/src/commands/onboard.ts +1868 -0
  17. package/packages/cli/src/commands/security.ts +144 -0
  18. package/packages/cli/src/commands/service.ts +50 -0
  19. package/packages/cli/src/commands/sessions.ts +116 -0
  20. package/packages/cli/src/commands/skills.ts +187 -0
  21. package/packages/cli/src/commands/update.ts +25 -0
  22. package/packages/cli/src/index.ts +185 -0
  23. package/packages/cli/src/utils/token.ts +6 -0
  24. package/packages/code-bridge/README.md +78 -0
  25. package/packages/code-bridge/package.json +18 -0
  26. package/packages/code-bridge/src/index.ts +95 -0
  27. package/packages/code-bridge/src/process-manager.ts +212 -0
  28. package/packages/code-bridge/src/schemas.ts +133 -0
  29. package/packages/core/package.json +46 -0
  30. package/packages/core/src/agent/agent-loop.ts +369 -0
  31. package/packages/core/src/agent/compaction.ts +140 -0
  32. package/packages/core/src/agent/context-compiler.ts +378 -0
  33. package/packages/core/src/agent/context-guard.ts +91 -0
  34. package/packages/core/src/agent/context.ts +138 -0
  35. package/packages/core/src/agent/conversation-store.ts +198 -0
  36. package/packages/core/src/agent/curator.ts +158 -0
  37. package/packages/core/src/agent/hooks.ts +166 -0
  38. package/packages/core/src/agent/index.ts +116 -0
  39. package/packages/core/src/agent/llm-client.ts +503 -0
  40. package/packages/core/src/agent/native-tools.ts +505 -0
  41. package/packages/core/src/agent/prompt-builder.ts +532 -0
  42. package/packages/core/src/agent/providers/index.ts +167 -0
  43. package/packages/core/src/agent/providers.ts +1 -0
  44. package/packages/core/src/agent/reflector.ts +170 -0
  45. package/packages/core/src/agent/service.ts +64 -0
  46. package/packages/core/src/agent/stuck-loop.ts +133 -0
  47. package/packages/core/src/agent/supervisor.ts +39 -0
  48. package/packages/core/src/agent/tracer.ts +102 -0
  49. package/packages/core/src/agent/workspace.ts +110 -0
  50. package/packages/core/src/canvas/canvas-manager.test.ts +161 -0
  51. package/packages/core/src/canvas/canvas-manager.ts +319 -0
  52. package/packages/core/src/canvas/canvas-tools.ts +420 -0
  53. package/packages/core/src/canvas/emitter.ts +115 -0
  54. package/packages/core/src/canvas/index.ts +2 -0
  55. package/packages/core/src/channels/base.ts +138 -0
  56. package/packages/core/src/channels/discord.ts +260 -0
  57. package/packages/core/src/channels/index.ts +7 -0
  58. package/packages/core/src/channels/manager.ts +383 -0
  59. package/packages/core/src/channels/slack.ts +287 -0
  60. package/packages/core/src/channels/telegram.ts +502 -0
  61. package/packages/core/src/channels/webchat.ts +128 -0
  62. package/packages/core/src/channels/whatsapp.ts +375 -0
  63. package/packages/core/src/config/index.ts +12 -0
  64. package/packages/core/src/config/loader.ts +529 -0
  65. package/packages/core/src/events/event-bus.ts +169 -0
  66. package/packages/core/src/gateway/index.ts +5 -0
  67. package/packages/core/src/gateway/initializer.ts +290 -0
  68. package/packages/core/src/gateway/lane-queue.ts +169 -0
  69. package/packages/core/src/gateway/resolver.ts +108 -0
  70. package/packages/core/src/gateway/router.ts +124 -0
  71. package/packages/core/src/gateway/server.ts +3317 -0
  72. package/packages/core/src/gateway/session.ts +95 -0
  73. package/packages/core/src/gateway/slash-commands.ts +192 -0
  74. package/packages/core/src/heartbeat/index.ts +157 -0
  75. package/packages/core/src/index.ts +19 -0
  76. package/packages/core/src/integrations/catalog.ts +286 -0
  77. package/packages/core/src/integrations/env.ts +64 -0
  78. package/packages/core/src/integrations/index.ts +2 -0
  79. package/packages/core/src/memory/index.ts +1 -0
  80. package/packages/core/src/memory/notes.ts +68 -0
  81. package/packages/core/src/plugins/api.ts +128 -0
  82. package/packages/core/src/plugins/index.ts +2 -0
  83. package/packages/core/src/plugins/loader.ts +365 -0
  84. package/packages/core/src/resilience/circuit-breaker.ts +225 -0
  85. package/packages/core/src/security/google-chat.ts +269 -0
  86. package/packages/core/src/security/index.ts +192 -0
  87. package/packages/core/src/security/pairing.ts +250 -0
  88. package/packages/core/src/security/rate-limit.ts +270 -0
  89. package/packages/core/src/security/signal.ts +321 -0
  90. package/packages/core/src/state/store.ts +312 -0
  91. package/packages/core/src/storage/bun-sqlite-store.ts +188 -0
  92. package/packages/core/src/storage/crypto.ts +101 -0
  93. package/packages/core/src/storage/db-context.ts +333 -0
  94. package/packages/core/src/storage/onboarding.ts +1087 -0
  95. package/packages/core/src/storage/schema.ts +541 -0
  96. package/packages/core/src/storage/seed.ts +571 -0
  97. package/packages/core/src/storage/sqlite.ts +387 -0
  98. package/packages/core/src/storage/usage.ts +212 -0
  99. package/packages/core/src/tools/bridge-events.ts +74 -0
  100. package/packages/core/src/tools/browser.ts +275 -0
  101. package/packages/core/src/tools/codebridge.ts +421 -0
  102. package/packages/core/src/tools/coordinator-tools.ts +179 -0
  103. package/packages/core/src/tools/cron.ts +611 -0
  104. package/packages/core/src/tools/exec.ts +140 -0
  105. package/packages/core/src/tools/fs.ts +364 -0
  106. package/packages/core/src/tools/index.ts +12 -0
  107. package/packages/core/src/tools/memory.ts +176 -0
  108. package/packages/core/src/tools/notify.ts +113 -0
  109. package/packages/core/src/tools/project-management.ts +376 -0
  110. package/packages/core/src/tools/project.ts +375 -0
  111. package/packages/core/src/tools/read.ts +158 -0
  112. package/packages/core/src/tools/web.ts +436 -0
  113. package/packages/core/src/tools/workspace.ts +171 -0
  114. package/packages/core/src/utils/benchmark.ts +80 -0
  115. package/packages/core/src/utils/crypto.ts +73 -0
  116. package/packages/core/src/utils/date.ts +42 -0
  117. package/packages/core/src/utils/index.ts +4 -0
  118. package/packages/core/src/utils/logger.ts +388 -0
  119. package/packages/core/src/utils/retry.ts +70 -0
  120. package/packages/core/src/voice/index.ts +583 -0
  121. package/packages/core/tsconfig.json +9 -0
  122. package/packages/mcp/package.json +26 -0
  123. package/packages/mcp/src/config.ts +13 -0
  124. package/packages/mcp/src/index.ts +1 -0
  125. package/packages/mcp/src/logger.ts +42 -0
  126. package/packages/mcp/src/manager.ts +434 -0
  127. package/packages/mcp/src/transports/index.ts +67 -0
  128. package/packages/mcp/src/transports/sse.ts +241 -0
  129. package/packages/mcp/src/transports/websocket.ts +159 -0
  130. package/packages/skills/package.json +21 -0
  131. package/packages/skills/src/bundled/agent_management/SKILL.md +24 -0
  132. package/packages/skills/src/bundled/browser_automation/SKILL.md +30 -0
  133. package/packages/skills/src/bundled/context_compact/SKILL.md +35 -0
  134. package/packages/skills/src/bundled/cron_manager/SKILL.md +52 -0
  135. package/packages/skills/src/bundled/file_manager/SKILL.md +76 -0
  136. package/packages/skills/src/bundled/http_client/SKILL.md +24 -0
  137. package/packages/skills/src/bundled/memory/SKILL.md +42 -0
  138. package/packages/skills/src/bundled/project_management/SKILL.md +26 -0
  139. package/packages/skills/src/bundled/shell/SKILL.md +43 -0
  140. package/packages/skills/src/bundled/system_notify/SKILL.md +52 -0
  141. package/packages/skills/src/bundled/voice/SKILL.md +25 -0
  142. package/packages/skills/src/bundled/web_search/SKILL.md +29 -0
  143. package/packages/skills/src/index.ts +1 -0
  144. package/packages/skills/src/loader.ts +282 -0
  145. package/packages/tools/package.json +43 -0
  146. package/packages/tools/src/browser/browser.test.ts +111 -0
  147. package/packages/tools/src/browser/index.ts +272 -0
  148. package/packages/tools/src/canvas/index.ts +220 -0
  149. package/packages/tools/src/cron/cron.test.ts +164 -0
  150. package/packages/tools/src/cron/index.ts +304 -0
  151. package/packages/tools/src/filesystem/filesystem.test.ts +240 -0
  152. package/packages/tools/src/filesystem/index.ts +379 -0
  153. package/packages/tools/src/git/index.ts +239 -0
  154. package/packages/tools/src/index.ts +4 -0
  155. package/packages/tools/src/shell/detect-env.ts +70 -0
  156. package/packages/tools/tsconfig.json +9 -0
@@ -0,0 +1,287 @@
1
+ import { App, ExpressReceiver, type SlashCommand } from "@slack/bolt";
2
+ import type { ChannelConfig, IncomingMessage, OutboundMessage } from "./base.ts";
3
+ import { BaseChannel } from "./base.ts";
4
+ import { logger } from "../utils/logger.ts";
5
+
6
+ export interface SlackConfig extends ChannelConfig {
7
+ botToken: string;
8
+ appToken: string;
9
+ signingSecret: string;
10
+ port?: number;
11
+ }
12
+
13
+ export interface SlackConnectionState {
14
+ status: "connecting" | "connected" | "disconnected" | "error";
15
+ error?: string;
16
+ }
17
+
18
+ export class SlackChannel extends BaseChannel {
19
+ name = "slack";
20
+ accountId: string;
21
+ config: SlackConfig;
22
+
23
+ private app: App | null = null;
24
+ private connectionState: SlackConnectionState = {
25
+ status: "disconnected",
26
+ };
27
+ private log = logger.child("slack");
28
+ private pendingMessages: Map<string, { ts: string; channel: string }> = new Map();
29
+
30
+ constructor(config: SlackConfig) {
31
+ super();
32
+ this.config = config;
33
+ const extractedAccountId = config.botToken.split(":")[0]?.replace("xoxb-", "");
34
+ if (!extractedAccountId) {
35
+ throw new Error("Could not determine account ID from Slack token");
36
+ }
37
+ this.accountId = extractedAccountId;
38
+ }
39
+
40
+ async start(): Promise<void> {
41
+ this.running = true;
42
+ this.connectionState.status = "connecting";
43
+
44
+ this.log.warn(`
45
+ ╔══════════════════════════════════════════════════════════════════╗
46
+ ║ SLACK CHANNEL SETUP REQUIREMENT ║
47
+ ║ ║
48
+ ║ Slack requires a PUBLIC URL for webhooks. ║
49
+ ║ For local development, use one of: ║
50
+ ║ ║
51
+ ║ 1. ngrok: ngrok http 3000 ║
52
+ ║ 2. Tailscale: tailscale fun ║
53
+ ║ 3. cloudflared tunnel --url http://localhost:3000 ║
54
+ ║ ║
55
+ ║ Then configure in Slack App settings: ║
56
+ ║ - Request URL: https://your-tunnel-url/slack/events ║
57
+ ╚══════════════════════════════════════════════════════════════════╝
58
+ `);
59
+
60
+ try {
61
+ const receiver = new ExpressReceiver({
62
+ signingSecret: this.config.signingSecret,
63
+ endpoints: "/slack/events",
64
+ processBeforeResponse: true,
65
+ });
66
+
67
+ this.app = new App({
68
+ token: this.config.botToken,
69
+ appToken: this.config.appToken,
70
+ receiver,
71
+ });
72
+
73
+ this.app.event("app_mention", async ({ event }) => {
74
+ await this.handleMention(event);
75
+ });
76
+
77
+ this.app.event("message", async ({ event }) => {
78
+ if ((event as any).channel_type === "im") {
79
+ await this.handleDirectMessage(event as any);
80
+ }
81
+ });
82
+
83
+ this.app.command("/ai", async ({ command, ack }) => {
84
+ await ack();
85
+ await this.handleSlashCommand(command);
86
+ });
87
+
88
+ const port = this.config.port ?? 3000;
89
+
90
+ await this.app.start(port);
91
+
92
+ this.connectionState.status = "connected";
93
+ this.log.info(`Slack channel started on port ${port}`);
94
+
95
+ } catch (error) {
96
+ this.connectionState.status = "error";
97
+ this.connectionState.error = (error as Error).message;
98
+ this.log.error(`Slack connection error: ${(error as Error).message}`);
99
+ throw error;
100
+ }
101
+ }
102
+
103
+ async stop(): Promise<void> {
104
+ this.running = false;
105
+
106
+ if (this.app) {
107
+ try {
108
+ await this.app.stop();
109
+ } catch {
110
+ // Ignore close errors
111
+ }
112
+ this.app = null;
113
+ }
114
+
115
+ this.connectionState.status = "disconnected";
116
+ this.log.info("Slack channel stopped");
117
+ }
118
+
119
+ private async handleMention(event: { user?: string; text?: string; channel?: string; ts?: string; files?: Array<{ url_private?: string; mimetype?: string; name?: string }> }): Promise<void> {
120
+ if (!event.user || !event.channel) return;
121
+
122
+ const content = event.text?.replace(/<@[A-Z0-9]+>/g, "").trim() || "";
123
+
124
+ const audioFile = event.files?.find(f =>
125
+ f.mimetype?.startsWith("audio/") ||
126
+ f.name?.endsWith(".mp3") ||
127
+ f.name?.endsWith(".wav") ||
128
+ f.name?.endsWith(".ogg") ||
129
+ f.name?.endsWith(".webm")
130
+ );
131
+
132
+ const incoming: IncomingMessage = {
133
+ sessionId: this.formatSessionId(event.channel, "group"),
134
+ channel: "slack",
135
+ accountId: this.accountId,
136
+ peerId: event.channel,
137
+ peerKind: "group",
138
+ content,
139
+ audio: audioFile?.url_private ? { url: audioFile.url_private, mimeType: audioFile.mimetype || "audio/webm" } : undefined,
140
+ metadata: {
141
+ userId: event.user,
142
+ timestamp: event.ts,
143
+ files: event.files,
144
+ },
145
+ };
146
+
147
+ await this.handleMessage(incoming);
148
+ }
149
+
150
+ private async handleDirectMessage(event: { user?: string; text?: string; channel?: string; ts?: string; files?: Array<{ url_private?: string; mimetype?: string; name?: string }> }): Promise<void> {
151
+ if (!event.user || !event.channel) return;
152
+ if (event.text?.startsWith("/")) return;
153
+
154
+ const audioFile = event.files?.find(f =>
155
+ f.mimetype?.startsWith("audio/") ||
156
+ f.name?.endsWith(".mp3") ||
157
+ f.name?.endsWith(".wav") ||
158
+ f.name?.endsWith(".ogg") ||
159
+ f.name?.endsWith(".webm")
160
+ );
161
+
162
+ const incoming: IncomingMessage = {
163
+ sessionId: this.formatSessionId(event.user, "direct"),
164
+ channel: "slack",
165
+ accountId: this.accountId,
166
+ peerId: event.user,
167
+ peerKind: "direct",
168
+ content: event.text || "",
169
+ audio: audioFile?.url_private ? { url: audioFile.url_private, mimeType: audioFile.mimetype || "audio/webm" } : undefined,
170
+ metadata: {
171
+ channel: event.channel,
172
+ timestamp: event.ts,
173
+ files: event.files,
174
+ },
175
+ };
176
+
177
+ await this.handleMessage(incoming);
178
+ }
179
+
180
+ private async handleSlashCommand(command: SlashCommand): Promise<void> {
181
+ const incoming: IncomingMessage = {
182
+ sessionId: this.formatSessionId(command.user_id, "direct"),
183
+ channel: "slack",
184
+ accountId: this.accountId,
185
+ peerId: command.user_id,
186
+ peerKind: "direct",
187
+ content: command.text,
188
+ metadata: {
189
+ channelId: command.channel_id,
190
+ command: command.command,
191
+ triggerId: command.trigger_id,
192
+ },
193
+ };
194
+
195
+ await this.handleMessage(incoming);
196
+ }
197
+
198
+ async startTyping(sessionId: string): Promise<void> {
199
+ if (!this.app) return;
200
+
201
+ const peerId = this.extractPeerId(sessionId);
202
+
203
+ try {
204
+ const result = await this.app.client.chat.postMessage({
205
+ channel: peerId,
206
+ text: "⏳ Procesando...",
207
+ });
208
+
209
+ if (result.ts && result.channel) {
210
+ this.pendingMessages.set(sessionId, { ts: result.ts as string, channel: result.channel as string });
211
+ }
212
+ } catch (error) {
213
+ this.log.debug(`Could not send typing placeholder: ${(error as Error).message}`);
214
+ }
215
+ }
216
+
217
+ async stopTyping(_sessionId: string): Promise<void> {
218
+ // No-op for Slack - we edit the message in send()
219
+ }
220
+
221
+ async send(sessionId: string, message: OutboundMessage): Promise<void> {
222
+ if (!this.app) {
223
+ throw new Error("Slack not connected");
224
+ }
225
+
226
+ const text = message.content ?? message.chunk ?? "";
227
+ if (!text) return;
228
+
229
+ const peerId = this.extractPeerId(sessionId);
230
+ const pending = this.pendingMessages.get(sessionId);
231
+
232
+ try {
233
+ if (pending) {
234
+ await this.app.client.chat.update({
235
+ channel: pending.channel,
236
+ ts: pending.ts,
237
+ text,
238
+ });
239
+ this.pendingMessages.delete(sessionId);
240
+ } else {
241
+ await this.app.client.chat.postMessage({
242
+ channel: peerId,
243
+ text,
244
+ });
245
+ }
246
+
247
+ this.log.debug(`Sent message to ${peerId}`);
248
+ } catch (error) {
249
+ this.log.error(`Failed to send Slack message: ${(error as Error).message}`);
250
+ throw error;
251
+ }
252
+ }
253
+
254
+ async sendAudio(sessionId: string, audio: Buffer, mimeType: string): Promise<void> {
255
+ if (!this.app) {
256
+ throw new Error("Slack not connected");
257
+ }
258
+
259
+ const peerId = this.extractPeerId(sessionId);
260
+
261
+ try {
262
+ await this.app.client.files.uploadV2({
263
+ channel_id: peerId,
264
+ file: audio,
265
+ filename: `response.${mimeType === "audio/mpeg" ? "mp3" : "webm"}`,
266
+ title: "Voice response",
267
+ });
268
+ this.log.debug(`Sent audio to ${peerId}`);
269
+ } catch (error) {
270
+ this.log.error(`Failed to send Slack audio: ${(error as Error).message}`);
271
+ throw error;
272
+ }
273
+ }
274
+
275
+ private extractPeerId(sessionId: string): string {
276
+ const parts = sessionId.split(":");
277
+ return parts[parts.length - 1] ?? "";
278
+ }
279
+
280
+ getState(): SlackConnectionState {
281
+ return { ...this.connectionState };
282
+ }
283
+ }
284
+
285
+ export function createSlackChannel(config: SlackConfig): SlackChannel {
286
+ return new SlackChannel(config);
287
+ }