verybot 0.1.3

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.

Potentially problematic release.


This version of verybot might be problematic. Click here for more details.

Files changed (244) hide show
  1. package/dist/brain/agent-registry.d.ts +75 -0
  2. package/dist/brain/agent-registry.js +124 -0
  3. package/dist/brain/agent.d.ts +146 -0
  4. package/dist/brain/agent.js +680 -0
  5. package/dist/brain/channel-store.d.ts +27 -0
  6. package/dist/brain/channel-store.js +78 -0
  7. package/dist/brain/compaction.d.ts +37 -0
  8. package/dist/brain/compaction.js +214 -0
  9. package/dist/brain/context.d.ts +33 -0
  10. package/dist/brain/context.js +77 -0
  11. package/dist/brain/delegation-store.d.ts +33 -0
  12. package/dist/brain/delegation-store.js +106 -0
  13. package/dist/brain/loop.d.ts +21 -0
  14. package/dist/brain/loop.js +161 -0
  15. package/dist/brain/mcp-adapter.d.ts +39 -0
  16. package/dist/brain/mcp-adapter.js +227 -0
  17. package/dist/brain/memory-extractor.d.ts +26 -0
  18. package/dist/brain/memory-extractor.js +82 -0
  19. package/dist/brain/providers.d.ts +10 -0
  20. package/dist/brain/providers.js +69 -0
  21. package/dist/brain/queue.d.ts +18 -0
  22. package/dist/brain/queue.js +84 -0
  23. package/dist/brain/run-tools.d.ts +47 -0
  24. package/dist/brain/run-tools.js +84 -0
  25. package/dist/brain/session-key.d.ts +23 -0
  26. package/dist/brain/session-key.js +41 -0
  27. package/dist/brain/session-state.d.ts +36 -0
  28. package/dist/brain/session-state.js +51 -0
  29. package/dist/brain/session-store.d.ts +50 -0
  30. package/dist/brain/session-store.js +207 -0
  31. package/dist/brain/session.d.ts +32 -0
  32. package/dist/brain/session.js +75 -0
  33. package/dist/brain/utils.d.ts +4 -0
  34. package/dist/brain/utils.js +26 -0
  35. package/dist/brain/worker-coordinator.d.ts +25 -0
  36. package/dist/brain/worker-coordinator.js +83 -0
  37. package/dist/channels/commands.d.ts +35 -0
  38. package/dist/channels/commands.js +65 -0
  39. package/dist/channels/discord/channel.d.ts +18 -0
  40. package/dist/channels/discord/channel.js +154 -0
  41. package/dist/channels/discord/markdown.d.ts +19 -0
  42. package/dist/channels/discord/markdown.js +62 -0
  43. package/dist/channels/manager.d.ts +29 -0
  44. package/dist/channels/manager.js +100 -0
  45. package/dist/channels/slack/channel.d.ts +26 -0
  46. package/dist/channels/slack/channel.js +207 -0
  47. package/dist/channels/slack/markdown.d.ts +19 -0
  48. package/dist/channels/slack/markdown.js +62 -0
  49. package/dist/channels/specs.d.ts +21 -0
  50. package/dist/channels/specs.js +96 -0
  51. package/dist/channels/telegram/channel.d.ts +18 -0
  52. package/dist/channels/telegram/channel.js +156 -0
  53. package/dist/channels/telegram/markdown.d.ts +17 -0
  54. package/dist/channels/telegram/markdown.js +66 -0
  55. package/dist/channels/types.d.ts +26 -0
  56. package/dist/channels/types.js +1 -0
  57. package/dist/channels/whatsapp/channel.d.ts +23 -0
  58. package/dist/channels/whatsapp/channel.js +242 -0
  59. package/dist/channels/whatsapp/markdown.d.ts +20 -0
  60. package/dist/channels/whatsapp/markdown.js +51 -0
  61. package/dist/cli/config.d.ts +5 -0
  62. package/dist/cli/config.js +78 -0
  63. package/dist/cli/index.d.ts +5 -0
  64. package/dist/cli/index.js +13 -0
  65. package/dist/computer/browser/actions.d.ts +31 -0
  66. package/dist/computer/browser/actions.js +148 -0
  67. package/dist/computer/browser/manager.d.ts +55 -0
  68. package/dist/computer/browser/manager.js +496 -0
  69. package/dist/computer/browser/profile-badge.d.ts +13 -0
  70. package/dist/computer/browser/profile-badge.js +67 -0
  71. package/dist/computer/browser/screenshot.d.ts +5 -0
  72. package/dist/computer/browser/screenshot.js +21 -0
  73. package/dist/computer/browser/snapshot.d.ts +30 -0
  74. package/dist/computer/browser/snapshot.js +242 -0
  75. package/dist/computer/browser/tools.d.ts +5 -0
  76. package/dist/computer/browser/tools.js +167 -0
  77. package/dist/computer/desktop/adapter.d.ts +25 -0
  78. package/dist/computer/desktop/adapter.js +11 -0
  79. package/dist/computer/desktop/macos.d.ts +24 -0
  80. package/dist/computer/desktop/macos.js +223 -0
  81. package/dist/computer/desktop/tools.d.ts +25 -0
  82. package/dist/computer/desktop/tools.js +114 -0
  83. package/dist/config/agent-config.d.ts +41 -0
  84. package/dist/config/agent-config.js +14 -0
  85. package/dist/config/model-catalog.d.ts +22 -0
  86. package/dist/config/model-catalog.js +99 -0
  87. package/dist/config/store.d.ts +25 -0
  88. package/dist/config/store.js +143 -0
  89. package/dist/config.d.ts +103 -0
  90. package/dist/config.js +224 -0
  91. package/dist/control-ui/assets/index-BANXNUyt.js +143 -0
  92. package/dist/control-ui/assets/index-BSUFrP9R.css +1 -0
  93. package/dist/control-ui/assets/noto-sans-cyrillic-ext-wght-normal-DSNfmdVt.woff2 +0 -0
  94. package/dist/control-ui/assets/noto-sans-cyrillic-wght-normal-B2hlT84T.woff2 +0 -0
  95. package/dist/control-ui/assets/noto-sans-devanagari-wght-normal-Cv-Vwajv.woff2 +0 -0
  96. package/dist/control-ui/assets/noto-sans-greek-ext-wght-normal-12T8GTDR.woff2 +0 -0
  97. package/dist/control-ui/assets/noto-sans-greek-wght-normal-Ymb6dZNd.woff2 +0 -0
  98. package/dist/control-ui/assets/noto-sans-latin-ext-wght-normal-W1qJv59z.woff2 +0 -0
  99. package/dist/control-ui/assets/noto-sans-latin-wght-normal-BYSzYMf3.woff2 +0 -0
  100. package/dist/control-ui/assets/noto-sans-vietnamese-wght-normal-DLTJy58D.woff2 +0 -0
  101. package/dist/control-ui/index.html +14 -0
  102. package/dist/control-ui/vite.svg +1 -0
  103. package/dist/events.d.ts +2 -0
  104. package/dist/events.js +11 -0
  105. package/dist/gateway/broadcast.d.ts +5 -0
  106. package/dist/gateway/broadcast.js +33 -0
  107. package/dist/gateway/methods/chat.d.ts +24 -0
  108. package/dist/gateway/methods/chat.js +19 -0
  109. package/dist/gateway/methods/config.d.ts +13 -0
  110. package/dist/gateway/methods/config.js +14 -0
  111. package/dist/gateway/methods/models.d.ts +10 -0
  112. package/dist/gateway/methods/models.js +14 -0
  113. package/dist/gateway/methods/prompt-templates.d.ts +23 -0
  114. package/dist/gateway/methods/prompt-templates.js +82 -0
  115. package/dist/gateway/methods/scheduler.d.ts +62 -0
  116. package/dist/gateway/methods/scheduler.js +129 -0
  117. package/dist/gateway/methods/sessions.d.ts +26 -0
  118. package/dist/gateway/methods/sessions.js +54 -0
  119. package/dist/gateway/methods/skills.d.ts +35 -0
  120. package/dist/gateway/methods/skills.js +202 -0
  121. package/dist/gateway/methods/system.d.ts +12 -0
  122. package/dist/gateway/methods/system.js +39 -0
  123. package/dist/gateway/methods/tasks.d.ts +21 -0
  124. package/dist/gateway/methods/tasks.js +46 -0
  125. package/dist/gateway/methods/teams.d.ts +70 -0
  126. package/dist/gateway/methods/teams.js +374 -0
  127. package/dist/gateway/methods/tools.d.ts +6 -0
  128. package/dist/gateway/methods/tools.js +7 -0
  129. package/dist/gateway/methods/whatsapp.d.ts +19 -0
  130. package/dist/gateway/methods/whatsapp.js +35 -0
  131. package/dist/gateway/rpc.d.ts +38 -0
  132. package/dist/gateway/rpc.js +75 -0
  133. package/dist/gateway/server.d.ts +4 -0
  134. package/dist/gateway/server.js +133 -0
  135. package/dist/index.d.ts +1 -0
  136. package/dist/index.js +212 -0
  137. package/dist/integrations/github.d.ts +7 -0
  138. package/dist/integrations/github.js +133 -0
  139. package/dist/integrations/mcp.d.ts +7 -0
  140. package/dist/integrations/mcp.js +106 -0
  141. package/dist/integrations/registry.d.ts +43 -0
  142. package/dist/integrations/registry.js +258 -0
  143. package/dist/integrations/scanner.d.ts +10 -0
  144. package/dist/integrations/scanner.js +122 -0
  145. package/dist/integrations/twitter.d.ts +10 -0
  146. package/dist/integrations/twitter.js +120 -0
  147. package/dist/integrations/types.d.ts +72 -0
  148. package/dist/integrations/types.js +1 -0
  149. package/dist/logger.d.ts +16 -0
  150. package/dist/logger.js +104 -0
  151. package/dist/markdown/chunk.d.ts +9 -0
  152. package/dist/markdown/chunk.js +52 -0
  153. package/dist/markdown/ir.d.ts +37 -0
  154. package/dist/markdown/ir.js +529 -0
  155. package/dist/markdown/render.d.ts +22 -0
  156. package/dist/markdown/render.js +148 -0
  157. package/dist/markdown/table-render.d.ts +43 -0
  158. package/dist/markdown/table-render.js +219 -0
  159. package/dist/markdown/tables.d.ts +17 -0
  160. package/dist/markdown/tables.js +27 -0
  161. package/dist/memory/embedding.d.ts +16 -0
  162. package/dist/memory/embedding.js +66 -0
  163. package/dist/memory/extractor.d.ts +6 -0
  164. package/dist/memory/extractor.js +72 -0
  165. package/dist/memory/search.d.ts +15 -0
  166. package/dist/memory/search.js +57 -0
  167. package/dist/memory/store.d.ts +34 -0
  168. package/dist/memory/store.js +328 -0
  169. package/dist/memory/types.d.ts +9 -0
  170. package/dist/memory/types.js +2 -0
  171. package/dist/paths.d.ts +20 -0
  172. package/dist/paths.js +29 -0
  173. package/dist/prompt-templates/builtins.d.ts +2 -0
  174. package/dist/prompt-templates/builtins.js +72 -0
  175. package/dist/prompt-templates/store.d.ts +39 -0
  176. package/dist/prompt-templates/store.js +174 -0
  177. package/dist/prompt-templates/types.d.ts +10 -0
  178. package/dist/prompt-templates/types.js +1 -0
  179. package/dist/scheduler/connected-channels.d.ts +24 -0
  180. package/dist/scheduler/connected-channels.js +57 -0
  181. package/dist/scheduler/scheduler.d.ts +22 -0
  182. package/dist/scheduler/scheduler.js +132 -0
  183. package/dist/scheduler/store.d.ts +27 -0
  184. package/dist/scheduler/store.js +205 -0
  185. package/dist/scheduler/types.d.ts +29 -0
  186. package/dist/scheduler/types.js +1 -0
  187. package/dist/security/command-validator.d.ts +22 -0
  188. package/dist/security/command-validator.js +160 -0
  189. package/dist/security/docker-sandbox.d.ts +48 -0
  190. package/dist/security/docker-sandbox.js +218 -0
  191. package/dist/security/env-filter.d.ts +8 -0
  192. package/dist/security/env-filter.js +41 -0
  193. package/dist/skills/loader.d.ts +33 -0
  194. package/dist/skills/loader.js +132 -0
  195. package/dist/skills/prompt.d.ts +6 -0
  196. package/dist/skills/prompt.js +17 -0
  197. package/dist/skills/read-tool.d.ts +7 -0
  198. package/dist/skills/read-tool.js +24 -0
  199. package/dist/skills/scanner.d.ts +6 -0
  200. package/dist/skills/scanner.js +73 -0
  201. package/dist/skills/types.d.ts +15 -0
  202. package/dist/skills/types.js +1 -0
  203. package/dist/tasks/store.d.ts +47 -0
  204. package/dist/tasks/store.js +193 -0
  205. package/dist/tasks/types.d.ts +75 -0
  206. package/dist/tasks/types.js +32 -0
  207. package/dist/teams/store.d.ts +78 -0
  208. package/dist/teams/store.js +420 -0
  209. package/dist/teams/types.d.ts +23 -0
  210. package/dist/teams/types.js +1 -0
  211. package/dist/tools/bash.d.ts +16 -0
  212. package/dist/tools/bash.js +62 -0
  213. package/dist/tools/channel-history.d.ts +10 -0
  214. package/dist/tools/channel-history.js +43 -0
  215. package/dist/tools/delegate.d.ts +16 -0
  216. package/dist/tools/delegate.js +216 -0
  217. package/dist/tools/fs.d.ts +4 -0
  218. package/dist/tools/fs.js +335 -0
  219. package/dist/tools/integration-toggle.d.ts +14 -0
  220. package/dist/tools/integration-toggle.js +47 -0
  221. package/dist/tools/memory.d.ts +13 -0
  222. package/dist/tools/memory.js +65 -0
  223. package/dist/tools/registry.d.ts +6 -0
  224. package/dist/tools/registry.js +9 -0
  225. package/dist/tools/schedule.d.ts +8 -0
  226. package/dist/tools/schedule.js +219 -0
  227. package/dist/tools/speak.d.ts +10 -0
  228. package/dist/tools/speak.js +56 -0
  229. package/dist/tools/tasks.d.ts +29 -0
  230. package/dist/tools/tasks.js +92 -0
  231. package/dist/tools/teams.d.ts +7 -0
  232. package/dist/tools/teams.js +180 -0
  233. package/dist/tools/web-fetch.d.ts +3 -0
  234. package/dist/tools/web-fetch.js +22 -0
  235. package/dist/tts/edge.d.ts +10 -0
  236. package/dist/tts/edge.js +60 -0
  237. package/dist/tts/speak.d.ts +12 -0
  238. package/dist/tts/speak.js +81 -0
  239. package/dist/tts/transcribe.d.ts +5 -0
  240. package/dist/tts/transcribe.js +40 -0
  241. package/dist/utils.d.ts +5 -0
  242. package/dist/utils.js +22 -0
  243. package/package.json +90 -0
  244. package/verybot.js +2 -0
@@ -0,0 +1,133 @@
1
+ import { createServer } from "node:http";
2
+ import { readFile, stat } from "node:fs/promises";
3
+ import { join, extname, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { WebSocketServer } from "ws";
6
+ import { addClient, broadcast } from "./broadcast.js";
7
+ import { createRpcDispatcher } from "./rpc.js";
8
+ import { on } from "../events.js";
9
+ import { logger } from "../logger.js";
10
+ /** Bridge app-level events to WebSocket broadcast. */
11
+ const BRIDGED_EVENTS = ["taskChange", "teamChange", "promptTemplateChange", "skillChange", "system", "agent", "chat", "whatsapp"];
12
+ for (const event of BRIDGED_EVENTS) {
13
+ on(event, (payload) => broadcast(event, payload));
14
+ }
15
+ const MIME_TYPES = {
16
+ ".html": "text/html",
17
+ ".js": "application/javascript",
18
+ ".css": "text/css",
19
+ ".json": "application/json",
20
+ ".png": "image/png",
21
+ ".jpg": "image/jpeg",
22
+ ".svg": "image/svg+xml",
23
+ ".ico": "image/x-icon",
24
+ ".woff": "font/woff",
25
+ ".woff2": "font/woff2",
26
+ ".ttf": "font/ttf",
27
+ };
28
+ /** Resolve the control-ui dist directory (lives next to compiled gateway code). */
29
+ function getUiDir() {
30
+ const thisFile = fileURLToPath(import.meta.url);
31
+ // dist/gateway/server.js -> dist/control-ui
32
+ return join(thisFile, "..", "..", "control-ui");
33
+ }
34
+ const SECURITY_HEADERS = {
35
+ "X-Content-Type-Options": "nosniff",
36
+ "X-Frame-Options": "DENY",
37
+ "Content-Security-Policy": "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; font-src 'self'",
38
+ };
39
+ async function serveStatic(req, res) {
40
+ const uiDir = resolve(getUiDir());
41
+ const url = req.url ?? "/";
42
+ const pathname = decodeURIComponent(url.split("?")[0]);
43
+ // Resolve and guard against path traversal
44
+ let filePath = resolve(uiDir, pathname.replace(/^\/+/, "") || "index.html");
45
+ if (!filePath.startsWith(uiDir)) {
46
+ res.writeHead(403);
47
+ res.end("Forbidden");
48
+ return;
49
+ }
50
+ try {
51
+ const fileStat = await stat(filePath);
52
+ if (fileStat.isDirectory()) {
53
+ filePath = join(filePath, "index.html");
54
+ }
55
+ const content = await readFile(filePath);
56
+ const ext = extname(filePath);
57
+ res.writeHead(200, { "Content-Type": MIME_TYPES[ext] ?? "application/octet-stream", ...SECURITY_HEADERS });
58
+ res.end(content);
59
+ }
60
+ catch {
61
+ // SPA fallback: serve index.html for non-asset routes
62
+ try {
63
+ const indexPath = join(uiDir, "index.html");
64
+ const content = await readFile(indexPath);
65
+ res.writeHead(200, { "Content-Type": "text/html", ...SECURITY_HEADERS });
66
+ res.end(content);
67
+ }
68
+ catch {
69
+ res.writeHead(404);
70
+ res.end("Not found");
71
+ }
72
+ }
73
+ }
74
+ export function startGateway(port, token, getAgent, opts = {}) {
75
+ const WS_PATH = "/ws";
76
+ const server = createServer((req, res) => serveStatic(req, res));
77
+ const wss = new WebSocketServer({ noServer: true });
78
+ const dispatch = createRpcDispatcher(getAgent, opts);
79
+ // Only upgrade requests on the /ws path
80
+ server.on("upgrade", (req, socket, head) => {
81
+ if (req.url === WS_PATH) {
82
+ wss.handleUpgrade(req, socket, head, (ws) => wss.emit("connection", ws, req));
83
+ }
84
+ else {
85
+ socket.destroy();
86
+ }
87
+ });
88
+ wss.on("connection", (ws) => {
89
+ let authed = false;
90
+ ws.on("message", async (raw) => {
91
+ let msg;
92
+ try {
93
+ msg = JSON.parse(raw.toString());
94
+ }
95
+ catch {
96
+ ws.send(JSON.stringify({ error: "invalid json" }));
97
+ return;
98
+ }
99
+ // Auth handshake
100
+ if (msg.method === "connect") {
101
+ if (msg.token === token) {
102
+ authed = true;
103
+ addClient(ws);
104
+ ws.send(JSON.stringify({ id: msg.id, result: { status: "ok" } }));
105
+ }
106
+ else {
107
+ ws.send(JSON.stringify({ id: msg.id, error: "auth failed" }));
108
+ ws.close();
109
+ }
110
+ return;
111
+ }
112
+ if (!authed) {
113
+ ws.send(JSON.stringify({ error: "not authenticated" }));
114
+ return;
115
+ }
116
+ // RPC dispatch
117
+ try {
118
+ const result = await dispatch(msg.method, msg.params, { ws });
119
+ ws.send(JSON.stringify({ id: msg.id, result }));
120
+ }
121
+ catch (err) {
122
+ ws.send(JSON.stringify({
123
+ id: msg.id,
124
+ error: err instanceof Error ? err.message : "unknown error",
125
+ }));
126
+ }
127
+ });
128
+ });
129
+ server.listen(port, () => {
130
+ logger.info(`Gateway listening on http://localhost:${port}`);
131
+ });
132
+ return server;
133
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,212 @@
1
+ import { handleCli } from "./cli/index.js";
2
+ import { loadConfig, seedConfigStore, injectSecretsIntoEnv } from "./config.js";
3
+ import { ConfigStore } from "./config/store.js";
4
+ import { BASE_DIR, SESSIONS_DIR, MEMORY_DB_PATH, SKILLS_DIR, ensureDirs } from "./paths.js";
5
+ import { startGateway } from "./gateway/server.js";
6
+ import { ChannelManager } from "./channels/manager.js";
7
+ import { Agent } from "./brain/agent.js";
8
+ import { ChannelStore } from "./brain/channel-store.js";
9
+ import { DelegationStore } from "./brain/delegation-store.js";
10
+ import { ToolRegistry } from "./tools/registry.js";
11
+ import { webFetchTool } from "./tools/web-fetch.js";
12
+ import { createFsTools } from "./tools/fs.js";
13
+ import { DockerSandbox } from "./security/docker-sandbox.js";
14
+ import { BrowserManager } from "./computer/browser/manager.js";
15
+ import { createBrowserTools } from "./computer/browser/tools.js";
16
+ import { createDesktopAdapter } from "./computer/desktop/adapter.js";
17
+ import { MemoryStore } from "./memory/store.js";
18
+ import { createEmbeddingProvider } from "./memory/embedding.js";
19
+ import { loadSkills } from "./skills/loader.js";
20
+ import { loadIntegrations } from "./integrations/registry.js";
21
+ import { ScheduleStore } from "./scheduler/store.js";
22
+ import { Scheduler } from "./scheduler/scheduler.js";
23
+ import { ConnectedChannelRegistry } from "./scheduler/connected-channels.js";
24
+ import { TaskStore } from "./tasks/store.js";
25
+ import { TeamStore } from "./teams/store.js";
26
+ import { PromptTemplateStore } from "./prompt-templates/store.js";
27
+ import { BUILTIN_TEMPLATES } from "./prompt-templates/builtins.js";
28
+ import { logger } from "./logger.js";
29
+ // --- CLI subcommand routing (no gateway boot) ---
30
+ if (handleCli())
31
+ process.exit(process.exitCode ?? 0);
32
+ // Prevent unhandled errors from crashing the process
33
+ process.on("uncaughtException", (err) => {
34
+ logger.error(`Uncaught exception: ${err instanceof Error ? err.stack ?? err.message : err}`);
35
+ });
36
+ process.on("unhandledRejection", (err) => {
37
+ logger.error(`Unhandled rejection: ${err instanceof Error ? err.stack ?? err.message : err}`);
38
+ });
39
+ async function teardown(rt) {
40
+ rt.scheduler.stop();
41
+ await rt.agent.flushMemories();
42
+ rt.agent.cleanupSandbox();
43
+ rt.skillManager.stopWatching();
44
+ await rt.integrationRegistry.cleanupAll();
45
+ await rt.browser.close();
46
+ rt.memoryStore?.close();
47
+ rt.scheduleStore.close();
48
+ rt.delegationStore?.close();
49
+ rt.channelStore?.close();
50
+ rt.taskStore.close();
51
+ rt.promptTemplateStore.close();
52
+ rt.teamStore.close();
53
+ await rt.channels.stopAll();
54
+ }
55
+ const SCHEDULER_TICK_MS = 5_000;
56
+ async function boot(configStore) {
57
+ injectSecretsIntoEnv(configStore);
58
+ const config = loadConfig(configStore);
59
+ logger.info("Config loaded");
60
+ // --- Docker Sandbox ---
61
+ const sandbox = config.sandbox.enabled ? new DockerSandbox(config.sandbox) : null;
62
+ // --- Tools ---
63
+ const registry = new ToolRegistry();
64
+ if (config.bash.security === "deny") {
65
+ logger.info("Bash tool disabled (BASH_SECURITY=deny)");
66
+ }
67
+ registry.register("web_fetch", webFetchTool);
68
+ // --- Filesystem ---
69
+ for (const [name, t] of Object.entries(createFsTools({ sandboxed: !!sandbox }))) {
70
+ registry.register(name, t);
71
+ }
72
+ // --- Browser ---
73
+ const browser = new BrowserManager({
74
+ headless: config.browserHeadless,
75
+ userAgent: config.browserUserAgent || undefined,
76
+ });
77
+ for (const [name, t] of Object.entries(createBrowserTools(browser))) {
78
+ registry.register(name, t);
79
+ }
80
+ // --- Desktop ---
81
+ let desktopAdapter = null;
82
+ try {
83
+ desktopAdapter = await createDesktopAdapter();
84
+ logger.info(`Desktop adapter ready (${process.platform})`);
85
+ }
86
+ catch (err) {
87
+ logger.error(`Desktop adapter failed to initialize: ${err instanceof Error ? err.message : err}`);
88
+ }
89
+ // --- Skills ---
90
+ const skillManager = await loadSkills(SKILLS_DIR);
91
+ // --- Integrations ---
92
+ const integrationRegistry = await loadIntegrations(config, configStore);
93
+ // --- Memory ---
94
+ let memoryStore = null;
95
+ let embeddingProvider = null;
96
+ if (config.memory.enabled) {
97
+ memoryStore = await MemoryStore.create(MEMORY_DB_PATH);
98
+ embeddingProvider = await createEmbeddingProvider("local");
99
+ logger.info(`Memory enabled (embeddings: ${embeddingProvider ? embeddingProvider.id : "none"})`);
100
+ }
101
+ // --- Scheduler Store ---
102
+ const scheduleStore = await ScheduleStore.create(MEMORY_DB_PATH);
103
+ // --- Task store ---
104
+ const taskStore = await TaskStore.create(MEMORY_DB_PATH);
105
+ // --- Prompt template store (must exist before TeamStore for FK) ---
106
+ const promptTemplateStore = await PromptTemplateStore.create(MEMORY_DB_PATH);
107
+ promptTemplateStore.seedBuiltins(BUILTIN_TEMPLATES);
108
+ // --- Team store (SQLite-backed, managed via UI/RPC) ---
109
+ const teamStore = await TeamStore.create(MEMORY_DB_PATH);
110
+ const teamConfigs = teamStore.toTeamConfigs();
111
+ // --- Multi-agent stores (TeamRegistry is created lazily by reloadConfig) ---
112
+ const hasWorkers = teamConfigs.some((t) => t.workers.length > 0);
113
+ let delegationStore = null;
114
+ let channelStore = null;
115
+ if (hasWorkers) {
116
+ delegationStore = await DelegationStore.create(MEMORY_DB_PATH);
117
+ channelStore = await ChannelStore.create(MEMORY_DB_PATH);
118
+ }
119
+ // --- Channels (created before Agent so it can reconcile them on config reload) ---
120
+ const channels = new ChannelManager();
121
+ // --- Connected Channels (scheduler live connections) ---
122
+ const connectedChannels = new ConnectedChannelRegistry();
123
+ // --- Agent ---
124
+ const agent = new Agent({
125
+ config,
126
+ configStore,
127
+ tools: registry.getAll(),
128
+ dataDir: SESSIONS_DIR,
129
+ memoryStore,
130
+ embeddingProvider,
131
+ sandbox,
132
+ skillManager,
133
+ integrationRegistry,
134
+ scheduleStore,
135
+ desktopAdapter,
136
+ browserManager: browser,
137
+ delegationStore,
138
+ channelStore,
139
+ taskStore,
140
+ teamStore,
141
+ channelManager: channels,
142
+ connectedChannels,
143
+ });
144
+ // Initial channel setup — reuses the same reconcile path as hot-reload
145
+ await agent.initChannels();
146
+ // --- Scheduler ---
147
+ const scheduler = new Scheduler(scheduleStore, agent, connectedChannels, SCHEDULER_TICK_MS);
148
+ scheduler.start();
149
+ logger.info("Runtime booted");
150
+ return { agent, scheduler, channels, connectedChannels, browser, skillManager, integrationRegistry, memoryStore, embeddingProvider, scheduleStore, delegationStore, channelStore, taskStore, promptTemplateStore, teamStore, sandbox };
151
+ }
152
+ /* ------------------------------------------------------------------ */
153
+ /* Main: gateway is long-lived, runtime is swappable */
154
+ /* ------------------------------------------------------------------ */
155
+ async function main() {
156
+ ensureDirs();
157
+ const configStore = new ConfigStore(BASE_DIR);
158
+ seedConfigStore(configStore);
159
+ // Mutable runtime ref — swapped on hot restart
160
+ let runtime = await boot(configStore);
161
+ /** Delay after teardown before booting — lets Telegram release the getUpdates long-poll. */
162
+ const RESTART_SETTLE_MS = 2_000;
163
+ const restart = async () => {
164
+ logger.info("Hot restart: tearing down...");
165
+ await teardown(runtime);
166
+ logger.info(`Hot restart: waiting ${RESTART_SETTLE_MS}ms for connections to settle...`);
167
+ await new Promise((r) => setTimeout(r, RESTART_SETTLE_MS));
168
+ logger.info("Hot restart: booting...");
169
+ runtime = await boot(configStore);
170
+ };
171
+ // Gateway survives restarts — RPC methods resolve agent via getter
172
+ const config = loadConfig(configStore);
173
+ startGateway(config.gateway.port, config.gateway.token, () => runtime.agent, {
174
+ configStore,
175
+ restart,
176
+ getTaskStore: () => runtime.taskStore,
177
+ getTeamStore: () => runtime.teamStore,
178
+ getScheduleStore: () => runtime.scheduleStore,
179
+ getConnectedChannels: () => runtime.connectedChannels,
180
+ getSkillManager: () => runtime.skillManager,
181
+ getMemoryStore: () => runtime.memoryStore,
182
+ getEmbeddingProvider: () => runtime.embeddingProvider,
183
+ getPromptTemplateStore: () => runtime.promptTemplateStore,
184
+ });
185
+ // --- Graceful shutdown ---
186
+ let shuttingDown = false;
187
+ const shutdown = async () => {
188
+ if (shuttingDown)
189
+ return process.exit(1);
190
+ shuttingDown = true;
191
+ logger.info("Shutting down...");
192
+ const forceTimer = setTimeout(() => {
193
+ logger.warn("Shutdown timed out, forcing exit");
194
+ process.exit(1);
195
+ }, 5_000);
196
+ forceTimer.unref();
197
+ try {
198
+ await teardown(runtime);
199
+ }
200
+ catch (err) {
201
+ logger.error(`Shutdown error: ${err}`);
202
+ }
203
+ process.exit(0);
204
+ };
205
+ process.on("SIGINT", shutdown);
206
+ process.on("SIGTERM", shutdown);
207
+ logger.info("Agent running. Waiting for messages...");
208
+ }
209
+ main().catch((err) => {
210
+ logger.error(`Fatal: ${err}`);
211
+ process.exit(1);
212
+ });
@@ -0,0 +1,7 @@
1
+ import type { Integration } from "./types.js";
2
+ interface GithubConfig {
3
+ token: string;
4
+ defaultOwner?: string;
5
+ }
6
+ export declare function createGithubIntegration(config: GithubConfig): Integration;
7
+ export {};
@@ -0,0 +1,133 @@
1
+ import { tool } from "ai";
2
+ import { z } from "zod";
3
+ import { Octokit } from "octokit";
4
+ import { logger } from "../logger.js";
5
+ const MAX_ISSUES_PER_PAGE = 30;
6
+ /** Zod schema for GitHub integration config. */
7
+ const githubConfigSchema = z.object({
8
+ token: z.string().min(1),
9
+ defaultOwner: z.string().optional(),
10
+ });
11
+ /** Flat config.json keys → GithubConfig fields. */
12
+ const GITHUB_CONFIG_KEYS = {
13
+ token: "GITHUB_TOKEN",
14
+ defaultOwner: "GITHUB_DEFAULT_OWNER",
15
+ };
16
+ export function createGithubIntegration(config) {
17
+ const octokit = new Octokit({ auth: config.token });
18
+ const tools = {
19
+ github_create_issue: tool({
20
+ description: "Create a new GitHub issue in a repository",
21
+ inputSchema: z.object({
22
+ owner: z.string().describe("Repository owner (user or org)"),
23
+ repo: z.string().describe("Repository name"),
24
+ title: z.string().describe("Issue title"),
25
+ body: z.string().optional().describe("Issue body (markdown)"),
26
+ labels: z.array(z.string()).optional().describe("Labels to apply"),
27
+ }),
28
+ execute: async ({ owner, repo, title, body, labels }) => {
29
+ try {
30
+ const { data } = await octokit.rest.issues.create({
31
+ owner: owner ?? config.defaultOwner ?? "",
32
+ repo,
33
+ title,
34
+ body,
35
+ labels,
36
+ });
37
+ return `Created issue #${data.number}: ${data.html_url}`;
38
+ }
39
+ catch (err) {
40
+ const msg = err instanceof Error ? err.message : String(err);
41
+ logger.error(`github_create_issue failed: ${msg}`);
42
+ return `Error creating issue: ${msg}`;
43
+ }
44
+ },
45
+ }),
46
+ github_list_issues: tool({
47
+ description: "List issues in a GitHub repository",
48
+ inputSchema: z.object({
49
+ owner: z.string().describe("Repository owner (user or org)"),
50
+ repo: z.string().describe("Repository name"),
51
+ state: z.enum(["open", "closed", "all"]).optional().describe("Filter by state (default: open)"),
52
+ labels: z.string().optional().describe("Comma-separated label names to filter by"),
53
+ limit: z
54
+ .number()
55
+ .optional()
56
+ .describe(`Max issues to return (default: ${MAX_ISSUES_PER_PAGE})`),
57
+ }),
58
+ execute: async ({ owner, repo, state, labels, limit }) => {
59
+ try {
60
+ const { data } = await octokit.rest.issues.listForRepo({
61
+ owner: owner ?? config.defaultOwner ?? "",
62
+ repo,
63
+ state: state ?? "open",
64
+ labels,
65
+ per_page: Math.min(limit ?? MAX_ISSUES_PER_PAGE, 100),
66
+ });
67
+ if (data.length === 0)
68
+ return "No issues found.";
69
+ return data
70
+ .map((i) => `#${i.number} [${i.state}] ${i.title} — ${i.html_url}`)
71
+ .join("\n");
72
+ }
73
+ catch (err) {
74
+ const msg = err instanceof Error ? err.message : String(err);
75
+ logger.error(`github_list_issues failed: ${msg}`);
76
+ return `Error listing issues: ${msg}`;
77
+ }
78
+ },
79
+ }),
80
+ github_create_pr: tool({
81
+ description: "Create a pull request in a GitHub repository",
82
+ inputSchema: z.object({
83
+ owner: z.string().describe("Repository owner (user or org)"),
84
+ repo: z.string().describe("Repository name"),
85
+ title: z.string().describe("PR title"),
86
+ body: z.string().optional().describe("PR description (markdown)"),
87
+ head: z.string().describe("Branch containing changes"),
88
+ base: z.string().describe("Branch to merge into (e.g., 'main')"),
89
+ }),
90
+ execute: async ({ owner, repo, title, body, head, base }) => {
91
+ try {
92
+ const { data } = await octokit.rest.pulls.create({
93
+ owner: owner ?? config.defaultOwner ?? "",
94
+ repo,
95
+ title,
96
+ body,
97
+ head,
98
+ base,
99
+ });
100
+ return `Created PR #${data.number}: ${data.html_url}`;
101
+ }
102
+ catch (err) {
103
+ const msg = err instanceof Error ? err.message : String(err);
104
+ logger.error(`github_create_pr failed: ${msg}`);
105
+ return `Error creating PR: ${msg}`;
106
+ }
107
+ },
108
+ }),
109
+ };
110
+ return {
111
+ id: "github",
112
+ name: "github",
113
+ source: "builtin",
114
+ tools: {
115
+ tools,
116
+ systemPrompt: [
117
+ "## GitHub Integration",
118
+ "You have GitHub tools available:",
119
+ "- `github_create_issue`: Create a new issue in a repo",
120
+ "- `github_list_issues`: List issues in a repo",
121
+ "- `github_create_pr`: Create a pull request",
122
+ ].join("\n"),
123
+ initialize: async () => {
124
+ const { data } = await octokit.rest.users.getAuthenticated();
125
+ logger.info(`GitHub integration ready (authenticated as ${data.login})`);
126
+ },
127
+ },
128
+ config: {
129
+ schema: githubConfigSchema,
130
+ configKeys: GITHUB_CONFIG_KEYS,
131
+ },
132
+ };
133
+ }
@@ -0,0 +1,7 @@
1
+ import type { McpServerConfig } from "../config.js";
2
+ import type { Integration } from "./types.js";
3
+ /**
4
+ * Create an Integration from an MCP server config.
5
+ * Connects to the server, discovers tools, and wraps them as Vercel AI SDK tools.
6
+ */
7
+ export declare function createMcpIntegration(name: string, config: McpServerConfig): Promise<Integration>;
@@ -0,0 +1,106 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3
+ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
4
+ import { tool, jsonSchema } from "ai";
5
+ import { logger } from "../logger.js";
6
+ /** Agent name sent to MCP servers during initialization. */
7
+ const CLIENT_NAME = "mvp-agent";
8
+ const CLIENT_VERSION = "1.0.0";
9
+ /**
10
+ * Format MCP tool result content array into a string for the model.
11
+ */
12
+ function formatMcpResult(content) {
13
+ if (!Array.isArray(content))
14
+ return String(content);
15
+ return content
16
+ .map((item) => {
17
+ if (item.type === "text")
18
+ return item.text;
19
+ if (item.type === "image")
20
+ return `[image: ${item.mimeType}]`;
21
+ if (item.type === "resource") {
22
+ const resource = item.resource;
23
+ return resource.text ?? `[resource: ${resource.uri}]`;
24
+ }
25
+ return JSON.stringify(item);
26
+ })
27
+ .join("\n");
28
+ }
29
+ /**
30
+ * Create an Integration from an MCP server config.
31
+ * Connects to the server, discovers tools, and wraps them as Vercel AI SDK tools.
32
+ */
33
+ export async function createMcpIntegration(name, config) {
34
+ const client = new Client({ name: CLIENT_NAME, version: CLIENT_VERSION }, { capabilities: {} });
35
+ let transport;
36
+ if (config.command && config.url) {
37
+ logger.warn(`MCP server "${name}": both "command" and "url" specified; using "command" (stdio)`);
38
+ }
39
+ if (config.command) {
40
+ transport = new StdioClientTransport({
41
+ command: config.command,
42
+ args: config.args ?? [],
43
+ env: { ...process.env, ...config.env },
44
+ });
45
+ }
46
+ else if (config.url) {
47
+ transport = new SSEClientTransport(new URL(config.url));
48
+ }
49
+ else {
50
+ throw new Error(`MCP server "${name}": config must have either "command" (stdio) or "url" (SSE)`);
51
+ }
52
+ // Connect and discover tools
53
+ await client.connect(transport);
54
+ const { tools: mcpTools } = await client.listTools();
55
+ // Convert MCP tools → Vercel AI SDK tools
56
+ const tools = {};
57
+ for (const mcpTool of mcpTools) {
58
+ const toolName = mcpTool.name;
59
+ tools[toolName] = tool({
60
+ description: mcpTool.description ?? `MCP tool: ${toolName}`,
61
+ inputSchema: jsonSchema(mcpTool.inputSchema),
62
+ execute: async (params) => {
63
+ try {
64
+ const result = await client.callTool({
65
+ name: toolName,
66
+ arguments: params,
67
+ });
68
+ if (result.isError) {
69
+ return `Error: ${formatMcpResult(result.content)}`;
70
+ }
71
+ return formatMcpResult(result.content);
72
+ }
73
+ catch (err) {
74
+ const msg = err instanceof Error ? err.message : String(err);
75
+ logger.error(`MCP tool "${toolName}" failed: ${msg}`);
76
+ return `Error calling ${toolName}: ${msg}`;
77
+ }
78
+ },
79
+ });
80
+ }
81
+ const toolNames = Object.keys(tools).join(", ");
82
+ return {
83
+ id: `mcp:${name}`,
84
+ name,
85
+ source: "mcp",
86
+ tools: {
87
+ tools,
88
+ systemPrompt: [
89
+ `## MCP: ${name}`,
90
+ `Connected MCP server with tools: ${toolNames}.`,
91
+ ].join("\n"),
92
+ initialize: async () => {
93
+ logger.info(`MCP server "${name}" connected (${config.command ? "stdio" : "sse"}, ${mcpTools.length} tools: ${toolNames})`);
94
+ },
95
+ cleanup: async () => {
96
+ try {
97
+ await client.close();
98
+ logger.info(`MCP server "${name}" disconnected`);
99
+ }
100
+ catch (err) {
101
+ logger.warn(`MCP server "${name}" cleanup error: ${err instanceof Error ? err.message : err}`);
102
+ }
103
+ },
104
+ },
105
+ };
106
+ }
@@ -0,0 +1,43 @@
1
+ import type { ToolSet } from "ai";
2
+ import type { ConfigStore } from "../config/store.js";
3
+ import type { Config } from "../config.js";
4
+ import type { Integration } from "./types.js";
5
+ /**
6
+ * Holds all available integrations (initialized at startup).
7
+ * Tools are NOT registered globally — they are injected per-session
8
+ * based on which integrations are active.
9
+ */
10
+ export declare class IntegrationRegistry {
11
+ private integrations;
12
+ register(integration: Integration): void;
13
+ get(name: string): Integration | undefined;
14
+ has(name: string): boolean;
15
+ /** Get all available integration names. */
16
+ get names(): string[];
17
+ /** Get tools for the given active integration names. */
18
+ getToolsFor(active: Set<string>): ToolSet;
19
+ /** Get system prompts for the given active integration names. */
20
+ getPromptsFor(active: Set<string>): string[];
21
+ /** Clean up all integrations that have a cleanup method (e.g. MCP connections). */
22
+ cleanupAll(): Promise<void>;
23
+ /**
24
+ * Re-discover builtin and user integrations from the ConfigStore.
25
+ * Adds newly-configured integrations, removes ones whose keys were deleted.
26
+ * MCP integrations are left untouched (they're long-lived connections).
27
+ */
28
+ refresh(store: ConfigStore, integrationsDir: string): Promise<void>;
29
+ /** Re-discover builtin integrations. */
30
+ private refreshBuiltins;
31
+ /** Re-discover user integrations from the integrations directory. */
32
+ private refreshUserIntegrations;
33
+ /** Build a compact listing of all available integrations for the system prompt. */
34
+ buildListing(active?: Set<string>): string;
35
+ }
36
+ /**
37
+ * Initialize all configured integrations and return a registry.
38
+ * Builtin integrations are auto-discovered via ConfigAdapter.configKeys.
39
+ * User integrations are loaded from the integrations directory.
40
+ * MCP servers are loaded from config.mcpServers.
41
+ * Continues past individual failures (logs error, skips that integration).
42
+ */
43
+ export declare function loadIntegrations(config: Config, store: ConfigStore): Promise<IntegrationRegistry>;