verybot 0.1.8

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 (277) hide show
  1. package/README.md +167 -0
  2. package/dist/aliases/store.d.ts +21 -0
  3. package/dist/aliases/store.js +148 -0
  4. package/dist/aliases/types.d.ts +6 -0
  5. package/dist/aliases/types.js +1 -0
  6. package/dist/brain/agent-registry.d.ts +96 -0
  7. package/dist/brain/agent-registry.js +141 -0
  8. package/dist/brain/agent.d.ts +167 -0
  9. package/dist/brain/agent.js +932 -0
  10. package/dist/brain/channel-store.d.ts +27 -0
  11. package/dist/brain/channel-store.js +78 -0
  12. package/dist/brain/compaction.d.ts +37 -0
  13. package/dist/brain/compaction.js +214 -0
  14. package/dist/brain/context.d.ts +43 -0
  15. package/dist/brain/context.js +139 -0
  16. package/dist/brain/delegation-store.d.ts +33 -0
  17. package/dist/brain/delegation-store.js +106 -0
  18. package/dist/brain/loop.d.ts +24 -0
  19. package/dist/brain/loop.js +318 -0
  20. package/dist/brain/mcp-adapter.d.ts +43 -0
  21. package/dist/brain/mcp-adapter.js +244 -0
  22. package/dist/brain/memory-extractor.d.ts +26 -0
  23. package/dist/brain/memory-extractor.js +82 -0
  24. package/dist/brain/providers.d.ts +14 -0
  25. package/dist/brain/providers.js +85 -0
  26. package/dist/brain/queue.d.ts +18 -0
  27. package/dist/brain/queue.js +111 -0
  28. package/dist/brain/run-tools.d.ts +50 -0
  29. package/dist/brain/run-tools.js +136 -0
  30. package/dist/brain/session-key.d.ts +23 -0
  31. package/dist/brain/session-key.js +41 -0
  32. package/dist/brain/session-state.d.ts +36 -0
  33. package/dist/brain/session-state.js +51 -0
  34. package/dist/brain/session-store.d.ts +50 -0
  35. package/dist/brain/session-store.js +207 -0
  36. package/dist/brain/session.d.ts +32 -0
  37. package/dist/brain/session.js +75 -0
  38. package/dist/brain/task-subscriber.d.ts +56 -0
  39. package/dist/brain/task-subscriber.js +317 -0
  40. package/dist/brain/user-content.d.ts +16 -0
  41. package/dist/brain/user-content.js +32 -0
  42. package/dist/brain/utils.d.ts +4 -0
  43. package/dist/brain/utils.js +26 -0
  44. package/dist/brain/worker-coordinator.d.ts +25 -0
  45. package/dist/brain/worker-coordinator.js +83 -0
  46. package/dist/channels/commands.d.ts +50 -0
  47. package/dist/channels/commands.js +132 -0
  48. package/dist/channels/discord/channel.d.ts +29 -0
  49. package/dist/channels/discord/channel.js +159 -0
  50. package/dist/channels/discord/markdown.d.ts +19 -0
  51. package/dist/channels/discord/markdown.js +62 -0
  52. package/dist/channels/manager.d.ts +29 -0
  53. package/dist/channels/manager.js +100 -0
  54. package/dist/channels/slack/channel.d.ts +37 -0
  55. package/dist/channels/slack/channel.js +227 -0
  56. package/dist/channels/slack/markdown.d.ts +19 -0
  57. package/dist/channels/slack/markdown.js +62 -0
  58. package/dist/channels/specs.d.ts +32 -0
  59. package/dist/channels/specs.js +99 -0
  60. package/dist/channels/telegram/channel.d.ts +29 -0
  61. package/dist/channels/telegram/channel.js +182 -0
  62. package/dist/channels/telegram/markdown.d.ts +17 -0
  63. package/dist/channels/telegram/markdown.js +66 -0
  64. package/dist/channels/types.d.ts +26 -0
  65. package/dist/channels/types.js +1 -0
  66. package/dist/channels/whatsapp/channel.d.ts +34 -0
  67. package/dist/channels/whatsapp/channel.js +276 -0
  68. package/dist/channels/whatsapp/markdown.d.ts +20 -0
  69. package/dist/channels/whatsapp/markdown.js +51 -0
  70. package/dist/cli/claude-login.d.ts +5 -0
  71. package/dist/cli/claude-login.js +47 -0
  72. package/dist/cli/config.d.ts +5 -0
  73. package/dist/cli/config.js +78 -0
  74. package/dist/cli/index.d.ts +11 -0
  75. package/dist/cli/index.js +96 -0
  76. package/dist/computer/browser/actions.d.ts +31 -0
  77. package/dist/computer/browser/actions.js +148 -0
  78. package/dist/computer/browser/context-manager.d.ts +28 -0
  79. package/dist/computer/browser/context-manager.js +78 -0
  80. package/dist/computer/browser/manager.d.ts +91 -0
  81. package/dist/computer/browser/manager.js +344 -0
  82. package/dist/computer/browser/profile-badge.d.ts +13 -0
  83. package/dist/computer/browser/profile-badge.js +67 -0
  84. package/dist/computer/browser/screenshot.d.ts +5 -0
  85. package/dist/computer/browser/screenshot.js +21 -0
  86. package/dist/computer/browser/snapshot.d.ts +30 -0
  87. package/dist/computer/browser/snapshot.js +242 -0
  88. package/dist/computer/browser/tools.d.ts +5 -0
  89. package/dist/computer/browser/tools.js +167 -0
  90. package/dist/computer/browser/types.d.ts +26 -0
  91. package/dist/computer/browser/types.js +1 -0
  92. package/dist/computer/desktop/adapter.d.ts +25 -0
  93. package/dist/computer/desktop/adapter.js +11 -0
  94. package/dist/computer/desktop/macos.d.ts +24 -0
  95. package/dist/computer/desktop/macos.js +223 -0
  96. package/dist/computer/desktop/tools.d.ts +25 -0
  97. package/dist/computer/desktop/tools.js +114 -0
  98. package/dist/config/agent-config.d.ts +55 -0
  99. package/dist/config/agent-config.js +16 -0
  100. package/dist/config/model-catalog.d.ts +22 -0
  101. package/dist/config/model-catalog.js +112 -0
  102. package/dist/config/model-spec.d.ts +8 -0
  103. package/dist/config/model-spec.js +66 -0
  104. package/dist/config/store.d.ts +25 -0
  105. package/dist/config/store.js +143 -0
  106. package/dist/config.d.ts +110 -0
  107. package/dist/config.js +259 -0
  108. package/dist/control-ui/assets/index-Cbl7G5Sc.css +1 -0
  109. package/dist/control-ui/assets/index-Cu1P4C62.js +266 -0
  110. package/dist/control-ui/assets/noto-sans-cyrillic-ext-wght-normal-DSNfmdVt.woff2 +0 -0
  111. package/dist/control-ui/assets/noto-sans-cyrillic-wght-normal-B2hlT84T.woff2 +0 -0
  112. package/dist/control-ui/assets/noto-sans-devanagari-wght-normal-Cv-Vwajv.woff2 +0 -0
  113. package/dist/control-ui/assets/noto-sans-greek-ext-wght-normal-12T8GTDR.woff2 +0 -0
  114. package/dist/control-ui/assets/noto-sans-greek-wght-normal-Ymb6dZNd.woff2 +0 -0
  115. package/dist/control-ui/assets/noto-sans-latin-ext-wght-normal-W1qJv59z.woff2 +0 -0
  116. package/dist/control-ui/assets/noto-sans-latin-wght-normal-BYSzYMf3.woff2 +0 -0
  117. package/dist/control-ui/assets/noto-sans-vietnamese-wght-normal-DLTJy58D.woff2 +0 -0
  118. package/dist/control-ui/index.html +14 -0
  119. package/dist/control-ui/vite.svg +1 -0
  120. package/dist/events.d.ts +2 -0
  121. package/dist/events.js +11 -0
  122. package/dist/gateway/broadcast.d.ts +5 -0
  123. package/dist/gateway/broadcast.js +33 -0
  124. package/dist/gateway/methods/aliases.d.ts +17 -0
  125. package/dist/gateway/methods/aliases.js +22 -0
  126. package/dist/gateway/methods/chat.d.ts +33 -0
  127. package/dist/gateway/methods/chat.js +37 -0
  128. package/dist/gateway/methods/config.d.ts +14 -0
  129. package/dist/gateway/methods/config.js +24 -0
  130. package/dist/gateway/methods/models.d.ts +10 -0
  131. package/dist/gateway/methods/models.js +14 -0
  132. package/dist/gateway/methods/playbooks.d.ts +45 -0
  133. package/dist/gateway/methods/playbooks.js +488 -0
  134. package/dist/gateway/methods/prompt-templates.d.ts +27 -0
  135. package/dist/gateway/methods/prompt-templates.js +106 -0
  136. package/dist/gateway/methods/scheduler.d.ts +62 -0
  137. package/dist/gateway/methods/scheduler.js +129 -0
  138. package/dist/gateway/methods/sessions.d.ts +44 -0
  139. package/dist/gateway/methods/sessions.js +111 -0
  140. package/dist/gateway/methods/system.d.ts +12 -0
  141. package/dist/gateway/methods/system.js +39 -0
  142. package/dist/gateway/methods/tasks.d.ts +40 -0
  143. package/dist/gateway/methods/tasks.js +151 -0
  144. package/dist/gateway/methods/teams.d.ts +69 -0
  145. package/dist/gateway/methods/teams.js +376 -0
  146. package/dist/gateway/methods/tools.d.ts +6 -0
  147. package/dist/gateway/methods/tools.js +7 -0
  148. package/dist/gateway/methods/whatsapp.d.ts +19 -0
  149. package/dist/gateway/methods/whatsapp.js +35 -0
  150. package/dist/gateway/rpc.d.ts +38 -0
  151. package/dist/gateway/rpc.js +79 -0
  152. package/dist/gateway/server.d.ts +9 -0
  153. package/dist/gateway/server.js +137 -0
  154. package/dist/index.d.ts +1 -0
  155. package/dist/index.js +254 -0
  156. package/dist/integrations/github.d.ts +7 -0
  157. package/dist/integrations/github.js +133 -0
  158. package/dist/integrations/mcp.d.ts +7 -0
  159. package/dist/integrations/mcp.js +106 -0
  160. package/dist/integrations/registry.d.ts +47 -0
  161. package/dist/integrations/registry.js +332 -0
  162. package/dist/integrations/scanner.d.ts +10 -0
  163. package/dist/integrations/scanner.js +122 -0
  164. package/dist/integrations/twitter.d.ts +10 -0
  165. package/dist/integrations/twitter.js +120 -0
  166. package/dist/integrations/types.d.ts +72 -0
  167. package/dist/integrations/types.js +1 -0
  168. package/dist/logger.d.ts +16 -0
  169. package/dist/logger.js +104 -0
  170. package/dist/markdown/chunk.d.ts +9 -0
  171. package/dist/markdown/chunk.js +52 -0
  172. package/dist/markdown/ir.d.ts +37 -0
  173. package/dist/markdown/ir.js +529 -0
  174. package/dist/markdown/render.d.ts +22 -0
  175. package/dist/markdown/render.js +148 -0
  176. package/dist/markdown/table-render.d.ts +43 -0
  177. package/dist/markdown/table-render.js +219 -0
  178. package/dist/markdown/tables.d.ts +17 -0
  179. package/dist/markdown/tables.js +27 -0
  180. package/dist/memory/embedding.d.ts +16 -0
  181. package/dist/memory/embedding.js +66 -0
  182. package/dist/memory/explicit.d.ts +16 -0
  183. package/dist/memory/explicit.js +29 -0
  184. package/dist/memory/extractor.d.ts +13 -0
  185. package/dist/memory/extractor.js +82 -0
  186. package/dist/memory/search.d.ts +15 -0
  187. package/dist/memory/search.js +57 -0
  188. package/dist/memory/session-learning.d.ts +23 -0
  189. package/dist/memory/session-learning.js +55 -0
  190. package/dist/memory/store.d.ts +36 -0
  191. package/dist/memory/store.js +334 -0
  192. package/dist/memory/types.d.ts +9 -0
  193. package/dist/memory/types.js +2 -0
  194. package/dist/paths.d.ts +28 -0
  195. package/dist/paths.js +48 -0
  196. package/dist/prompt-templates/builtins/index.d.ts +4 -0
  197. package/dist/prompt-templates/builtins/index.js +5 -0
  198. package/dist/prompt-templates/builtins/planner.d.ts +4 -0
  199. package/dist/prompt-templates/builtins/planner.js +77 -0
  200. package/dist/prompt-templates/store.d.ts +45 -0
  201. package/dist/prompt-templates/store.js +224 -0
  202. package/dist/prompt-templates/types.d.ts +10 -0
  203. package/dist/prompt-templates/types.js +1 -0
  204. package/dist/scheduler/connected-channels.d.ts +24 -0
  205. package/dist/scheduler/connected-channels.js +57 -0
  206. package/dist/scheduler/scheduler.d.ts +22 -0
  207. package/dist/scheduler/scheduler.js +132 -0
  208. package/dist/scheduler/store.d.ts +27 -0
  209. package/dist/scheduler/store.js +205 -0
  210. package/dist/scheduler/types.d.ts +29 -0
  211. package/dist/scheduler/types.js +1 -0
  212. package/dist/security/command-validator.d.ts +22 -0
  213. package/dist/security/command-validator.js +160 -0
  214. package/dist/security/docker-sandbox.d.ts +48 -0
  215. package/dist/security/docker-sandbox.js +218 -0
  216. package/dist/security/env-filter.d.ts +8 -0
  217. package/dist/security/env-filter.js +41 -0
  218. package/dist/skills/loader.d.ts +33 -0
  219. package/dist/skills/loader.js +132 -0
  220. package/dist/skills/prompt.d.ts +6 -0
  221. package/dist/skills/prompt.js +17 -0
  222. package/dist/skills/read-tool.d.ts +7 -0
  223. package/dist/skills/read-tool.js +24 -0
  224. package/dist/skills/scanner.d.ts +6 -0
  225. package/dist/skills/scanner.js +73 -0
  226. package/dist/skills/types.d.ts +15 -0
  227. package/dist/skills/types.js +1 -0
  228. package/dist/tasks/inline-attachment-content.d.ts +9 -0
  229. package/dist/tasks/inline-attachment-content.js +64 -0
  230. package/dist/tasks/store.d.ts +112 -0
  231. package/dist/tasks/store.js +519 -0
  232. package/dist/tasks/types.d.ts +129 -0
  233. package/dist/tasks/types.js +80 -0
  234. package/dist/teams/status-config.d.ts +8 -0
  235. package/dist/teams/status-config.js +40 -0
  236. package/dist/teams/store.d.ts +111 -0
  237. package/dist/teams/store.js +671 -0
  238. package/dist/teams/types.d.ts +30 -0
  239. package/dist/teams/types.js +1 -0
  240. package/dist/tools/bash.d.ts +18 -0
  241. package/dist/tools/bash.js +64 -0
  242. package/dist/tools/channel-history.d.ts +10 -0
  243. package/dist/tools/channel-history.js +43 -0
  244. package/dist/tools/delegate.d.ts +20 -0
  245. package/dist/tools/delegate.js +299 -0
  246. package/dist/tools/fs.d.ts +4 -0
  247. package/dist/tools/fs.js +335 -0
  248. package/dist/tools/integration-toggle.d.ts +14 -0
  249. package/dist/tools/integration-toggle.js +47 -0
  250. package/dist/tools/memory.d.ts +13 -0
  251. package/dist/tools/memory.js +59 -0
  252. package/dist/tools/prompt-templates.d.ts +7 -0
  253. package/dist/tools/prompt-templates.js +133 -0
  254. package/dist/tools/registry.d.ts +6 -0
  255. package/dist/tools/registry.js +9 -0
  256. package/dist/tools/schedule.d.ts +8 -0
  257. package/dist/tools/schedule.js +219 -0
  258. package/dist/tools/speak.d.ts +10 -0
  259. package/dist/tools/speak.js +56 -0
  260. package/dist/tools/tasks.d.ts +67 -0
  261. package/dist/tools/tasks.js +288 -0
  262. package/dist/tools/teams.d.ts +22 -0
  263. package/dist/tools/teams.js +470 -0
  264. package/dist/tools/web-fetch.d.ts +3 -0
  265. package/dist/tools/web-fetch.js +22 -0
  266. package/dist/tts/edge.d.ts +10 -0
  267. package/dist/tts/edge.js +60 -0
  268. package/dist/tts/speak.d.ts +12 -0
  269. package/dist/tts/speak.js +81 -0
  270. package/dist/tts/transcribe.d.ts +5 -0
  271. package/dist/tts/transcribe.js +40 -0
  272. package/dist/utils.d.ts +5 -0
  273. package/dist/utils.js +22 -0
  274. package/dist/version.d.ts +1 -0
  275. package/dist/version.js +13 -0
  276. package/package.json +102 -0
  277. package/verybot.js +2 -0
@@ -0,0 +1,30 @@
1
+ import type { TaskStatusConfig } from "../config/agent-config.js";
2
+ export interface Team {
3
+ id: string;
4
+ name: string;
5
+ color: string;
6
+ workspace: string;
7
+ /** Custom task statuses (null/undefined = defaults). */
8
+ statuses?: TaskStatusConfig[];
9
+ createdAt: number;
10
+ updatedAt: number;
11
+ }
12
+ export interface AgentRow {
13
+ id: string;
14
+ teamId: string;
15
+ name: string;
16
+ role: "orchestrator" | "worker";
17
+ model: string;
18
+ contextWindow: number;
19
+ maxSteps: number;
20
+ identity: string;
21
+ tools: string[];
22
+ timeout: number;
23
+ templateId: string | null;
24
+ /** Task statuses this agent subscribes to for pull-based execution. */
25
+ subscriptions: string[];
26
+ /** Max concurrent tasks this agent can run simultaneously. */
27
+ concurrency: number;
28
+ createdAt: number;
29
+ updatedAt: number;
30
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import { type Tool } from "ai";
2
+ import type { BashConfig } from "../config.js";
3
+ import type { DockerSandbox } from "../security/docker-sandbox.js";
4
+ /** Per-run context injected when creating the bash tool inside Agent.run(). */
5
+ export interface BashToolContext {
6
+ sessionKey: string;
7
+ sandbox: DockerSandbox | null;
8
+ /** Optional host working directory for command execution. */
9
+ cwd?: string;
10
+ }
11
+ /**
12
+ * Create a bash tool with the given security config and optional per-run context.
13
+ * Returns null in "deny" mode (tool not registered at all).
14
+ *
15
+ * When `ctx.sandbox` is provided, commands execute inside a Docker container.
16
+ * Otherwise, commands execute on the host with sanitized environment.
17
+ */
18
+ export declare function createBashTool(config: BashConfig, ctx?: BashToolContext): Tool | null;
@@ -0,0 +1,64 @@
1
+ import { tool } from "ai";
2
+ import { z } from "zod";
3
+ import { execSync } from "child_process";
4
+ import { validateCommand, isAllowed, DEFAULT_SAFE_BINS } from "../security/command-validator.js";
5
+ import { sanitizeEnv } from "../security/env-filter.js";
6
+ import { logger } from "../logger.js";
7
+ const EXEC_TIMEOUT = 30_000;
8
+ const MAX_OUTPUT = 10_000;
9
+ const MAX_ERROR = 5_000;
10
+ /**
11
+ * Create a bash tool with the given security config and optional per-run context.
12
+ * Returns null in "deny" mode (tool not registered at all).
13
+ *
14
+ * When `ctx.sandbox` is provided, commands execute inside a Docker container.
15
+ * Otherwise, commands execute on the host with sanitized environment.
16
+ */
17
+ export function createBashTool(config, ctx) {
18
+ if (config.security === "deny")
19
+ return null;
20
+ // Merge user-provided safe bins with defaults
21
+ const safeBins = new Set([...DEFAULT_SAFE_BINS, ...config.safeBins]);
22
+ return tool({
23
+ description: "Execute a shell command. Confirm with user before destructive commands (rm, mv, chmod). " +
24
+ "Backticks, $(), redirects, and subshells are blocked.",
25
+ inputSchema: z.object({
26
+ command: z.string().describe("The shell command to execute"),
27
+ }),
28
+ execute: async ({ command }) => {
29
+ // 1. Always validate syntax (blocks dangerous tokens in all modes)
30
+ const validation = validateCommand(command);
31
+ if (!validation.ok) {
32
+ logger.warn(`Bash blocked (syntax): ${validation.reason} — ${command}`);
33
+ return `Blocked: ${validation.reason}`;
34
+ }
35
+ // 2. In allowlist mode, check executables against safe bins + allowlist
36
+ if (config.security === "allowlist") {
37
+ if (!isAllowed(command, safeBins, config.allowlist)) {
38
+ logger.warn(`Bash blocked (allowlist): ${command}`);
39
+ return `Blocked: command not in allowlist. Safe bins: ${[...safeBins].join(", ")}`;
40
+ }
41
+ }
42
+ // 3. Execute — sandbox if available, otherwise host
43
+ if (ctx?.sandbox) {
44
+ return ctx.sandbox.exec(ctx.sessionKey, command);
45
+ }
46
+ try {
47
+ const cwd = ctx?.cwd?.trim() || undefined;
48
+ const output = execSync(command, {
49
+ encoding: "utf-8",
50
+ timeout: EXEC_TIMEOUT,
51
+ maxBuffer: 1024 * 1024,
52
+ env: sanitizeEnv(),
53
+ cwd,
54
+ });
55
+ return output.slice(0, MAX_OUTPUT);
56
+ }
57
+ catch (err) {
58
+ const msg = err instanceof Error ? err.message : String(err);
59
+ const stderr = err.stderr ?? "";
60
+ return `Error: ${msg}\n${stderr}`.slice(0, MAX_ERROR);
61
+ }
62
+ },
63
+ });
64
+ }
@@ -0,0 +1,10 @@
1
+ import { type Tool } from "ai";
2
+ import type { ChannelManager } from "../channels/manager.js";
3
+ /**
4
+ * Create a read_channel_history tool that lets the LLM fetch recent messages
5
+ * from the current messaging channel (Slack, etc.) on demand.
6
+ *
7
+ * The channelId is resolved fresh at execution time from a callback
8
+ * to avoid stale closures.
9
+ */
10
+ export declare function createChannelHistoryTool(channelManager: ChannelManager, channelType: string, resolveChannelId: () => string): Tool | null;
@@ -0,0 +1,43 @@
1
+ import { tool } from "ai";
2
+ import { z } from "zod";
3
+ import { logger } from "../logger.js";
4
+ /** Default number of messages to fetch. */
5
+ const DEFAULT_LIMIT = 20;
6
+ /**
7
+ * Create a read_channel_history tool that lets the LLM fetch recent messages
8
+ * from the current messaging channel (Slack, etc.) on demand.
9
+ *
10
+ * The channelId is resolved fresh at execution time from a callback
11
+ * to avoid stale closures.
12
+ */
13
+ export function createChannelHistoryTool(channelManager, channelType, resolveChannelId) {
14
+ const ch = channelManager.get(channelType);
15
+ if (!ch?.readHistory)
16
+ return null;
17
+ return tool({
18
+ description: "Read recent messages from the current chat channel or thread. " +
19
+ "Use this when the user references prior conversation, asks what was discussed, " +
20
+ "or needs context about the channel's recent messages. " +
21
+ "Returns messages in chronological order.",
22
+ inputSchema: z.object({
23
+ limit: z
24
+ .number()
25
+ .int()
26
+ .min(1)
27
+ .max(50)
28
+ .optional()
29
+ .describe("Number of recent messages to fetch (default 20, max 50)"),
30
+ }),
31
+ execute: async ({ limit }) => {
32
+ const channelId = resolveChannelId();
33
+ logger.info(`[channel-history] Reading ${channelType}/${channelId} limit=${limit ?? DEFAULT_LIMIT}`);
34
+ const messages = await ch.readHistory(channelId, limit ?? DEFAULT_LIMIT);
35
+ if (messages.length === 0)
36
+ return `No messages found in channel ${channelId}.`;
37
+ const formatted = messages
38
+ .map((m) => `<@${m.user}>: ${m.text}`)
39
+ .join("\n");
40
+ return `[${channelType} channel ${channelId} — ${messages.length} messages]\n${formatted}`;
41
+ },
42
+ });
43
+ }
@@ -0,0 +1,20 @@
1
+ import { type ToolSet } from "ai";
2
+ import type { AgentRegistry } from "../brain/agent-registry.js";
3
+ import type { ChannelStore } from "../brain/channel-store.js";
4
+ import type { DelegationStore } from "../brain/delegation-store.js";
5
+ import type { SessionStore } from "../brain/session-store.js";
6
+ import type { MemoryStore } from "../memory/store.js";
7
+ import type { EmbeddingProvider } from "../memory/embedding.js";
8
+ import { type BrowserConfig } from "../computer/browser/manager.js";
9
+ /**
10
+ * Creates delegation tools:
11
+ * - `delegate` — fires a worker in background, returns channel ID immediately
12
+ * - `list_workers` — lists worker names
13
+ * - `worker_get` — gets full config for one worker
14
+ * - `read_channel` — reads all messages from a channel (available to orchestrator AND workers)
15
+ */
16
+ export declare function createDelegationTools(registry: AgentRegistry, channelStore: ChannelStore, delegationStore: DelegationStore, sessionStore: SessionStore, orchestratorId: string, sessionKey: string, memoryStore: MemoryStore | null, embeddingProvider: EmbeddingProvider | null, memoryMaxResults: number, onWorkerComplete: (sessionKey: string, channelId: string) => void, browserConfig: BrowserConfig | null,
17
+ /** Human-readable session label for logs (team name instead of UUID). */
18
+ sessionLabel?: string,
19
+ /** Whether docker sandboxing is enabled (forwarded to MCP adapter). */
20
+ sandboxEnabled?: boolean): ToolSet;
@@ -0,0 +1,299 @@
1
+ import { randomUUID } from "crypto";
2
+ import { tool } from "ai";
3
+ import { z } from "zod";
4
+ import { Session } from "../brain/session.js";
5
+ import { buildSystemPrompt } from "../brain/context.js";
6
+ import { runLoop } from "../brain/loop.js";
7
+ import { adaptTools } from "../brain/mcp-adapter.js";
8
+ import { buildUserMessageContent } from "../brain/user-content.js";
9
+ import { createMemorySearchTool } from "./memory.js";
10
+ import { emit } from "../events.js";
11
+ import { logger } from "../logger.js";
12
+ import { resolveInlineAttachmentContent } from "../tasks/inline-attachment-content.js";
13
+ import { BrowserManager } from "../computer/browser/manager.js";
14
+ import { createBrowserTools, BROWSER_TOOL_NAMES } from "../computer/browser/tools.js";
15
+ import { DEFAULT_WORKER_TIMEOUT_S } from "../config/agent-config.js";
16
+ import { parseModelSpec } from "../config/model-spec.js";
17
+ /**
18
+ * Creates delegation tools:
19
+ * - `delegate` — fires a worker in background, returns channel ID immediately
20
+ * - `list_workers` — lists worker names
21
+ * - `worker_get` — gets full config for one worker
22
+ * - `read_channel` — reads all messages from a channel (available to orchestrator AND workers)
23
+ */
24
+ export function createDelegationTools(registry, channelStore, delegationStore, sessionStore, orchestratorId, sessionKey, memoryStore, embeddingProvider, memoryMaxResults, onWorkerComplete, browserConfig,
25
+ /** Human-readable session label for logs (team name instead of UUID). */
26
+ sessionLabel,
27
+ /** Whether docker sandboxing is enabled (forwarded to MCP adapter). */
28
+ sandboxEnabled = false) {
29
+ const delegatable = registry.delegatableWorkers();
30
+ const idToName = registry.buildIdToNameMap();
31
+ const listWorkersToolName = "list_workers";
32
+ const workerGetToolName = "worker_get";
33
+ const delegate = tool({
34
+ description: `Delegate a task to a specialized worker agent. Returns immediately with a channel ID — the worker runs in the background. ` +
35
+ `You will be automatically notified when workers finish. Use read_channel to see results. ` +
36
+ `Use ${listWorkersToolName} to discover available worker names. ` +
37
+ `You can fire multiple delegations in the same step — they all run concurrently. ` +
38
+ `Pass contextChannels to give the worker read access to other workers' channels.`,
39
+ inputSchema: z.object({
40
+ worker: z
41
+ .string()
42
+ .describe("Which worker name to delegate to"),
43
+ task: z
44
+ .string()
45
+ .describe("Self-contained task description for the worker"),
46
+ contextChannels: z
47
+ .array(z.string())
48
+ .optional()
49
+ .describe("Channel IDs the worker can read for context from other workers"),
50
+ }),
51
+ execute: async ({ worker, task, contextChannels }) => {
52
+ const resolved = registry.resolveWorker(worker);
53
+ if (!resolved) {
54
+ return `Unknown worker: ${worker}. Call ${listWorkersToolName} to see available workers.`;
55
+ }
56
+ const { agentConfig, model, tools: workerBaseTools } = resolved;
57
+ const agentId = agentConfig.id;
58
+ const agentName = agentConfig.name;
59
+ // Create channel for this delegation
60
+ const channelId = channelStore.createChannel();
61
+ channelStore.post(channelId, orchestratorId, "task", task);
62
+ // Record in delegation store (uses DB id for storage)
63
+ const delegationId = randomUUID().slice(0, 8);
64
+ delegationStore.insert({
65
+ id: delegationId,
66
+ agentId,
67
+ sessionKey,
68
+ task,
69
+ channelId,
70
+ status: "running",
71
+ createdAt: Date.now(),
72
+ });
73
+ // Worker tools: base (filtered) + memory_search (read-only) + read_channel
74
+ // Give worker its own browser instance (temp profile) so it doesn't conflict with others
75
+ const hasBrowserTools = BROWSER_TOOL_NAMES.some((n) => n in workerBaseTools);
76
+ let workerBrowser = null;
77
+ const workerTools = { ...workerBaseTools };
78
+ if (hasBrowserTools && browserConfig) {
79
+ // Strip shared browser tools, replace with per-worker instance
80
+ for (const n of BROWSER_TOOL_NAMES)
81
+ delete workerTools[n];
82
+ workerBrowser = new BrowserManager({ ...browserConfig, profileDir: "temp" });
83
+ Object.assign(workerTools, createBrowserTools(workerBrowser));
84
+ }
85
+ if (memoryStore) {
86
+ workerTools.memory_search = createMemorySearchTool(memoryStore, embeddingProvider, memoryMaxResults);
87
+ }
88
+ // Workers can read channels (their own + any context channels)
89
+ workerTools.read_channel = createReadChannelTool(channelStore, idToName);
90
+ // Build context hint about available channels
91
+ let channelHint = "";
92
+ if (contextChannels?.length) {
93
+ channelHint = `\n\nYou have access to read_channel. Context channels from other workers: ${contextChannels.join(", ")}. Call read_channel to see their results before starting your work.`;
94
+ }
95
+ const system = buildSystemPrompt({
96
+ identity: agentConfig.identity,
97
+ hasMemory: memoryStore !== null,
98
+ });
99
+ // Worker key extends parent key: {parentSessionKey}:{workerName}:{timestamp}
100
+ const workerSessionKey = `${sessionKey}:${agentName}:${Date.now()}`;
101
+ const session = new Session(workerSessionKey);
102
+ const workerTaskInput = task + channelHint;
103
+ const { normalizedText, imageDataUrls } = await resolveInlineAttachmentContent(workerTaskInput);
104
+ session.append({
105
+ role: "user",
106
+ content: buildUserMessageContent(normalizedText, imageDataUrls),
107
+ });
108
+ logger.info(`[delegate] ${orchestratorId} -> ${agentName}: ${task.slice(0, 200)} (channel: ${channelId})`);
109
+ emit("agent", {
110
+ sessionKey,
111
+ delegation: { agentId, agentName, channelId, status: "started" },
112
+ });
113
+ // Fire worker in background with timeout — does NOT await
114
+ const workerMaxSteps = agentConfig.maxSteps > 0 ? agentConfig.maxSteps : undefined;
115
+ const workerLabel = sessionLabel ? `worker:${sessionLabel}:${agentName}` : undefined;
116
+ const parsedWorkerModel = parseModelSpec(agentConfig.model);
117
+ const workerProvider = parsedWorkerModel.provider;
118
+ const workerModelId = parsedWorkerModel.modelId;
119
+ const workerCodexReasoningEffort = parsedWorkerModel.codexReasoningEffort;
120
+ // Extract team name from sessionLabel (format: "TeamName:channel:id...") for worker metadata
121
+ const teamName = sessionLabel?.split(":")[0];
122
+ const timeoutMs = (agentConfig.timeout ?? DEFAULT_WORKER_TIMEOUT_S) * 1_000;
123
+ withTimeout(runWorker(agentId, agentName, model, workerProvider, workerModelId, workerCodexReasoningEffort, system, session, workerTools, workerSessionKey, workerMaxSteps, workerLabel, sandboxEnabled), timeoutMs, `Worker ${agentName} timed out after ${timeoutMs / 1_000}s`).then(({ text: result, responseMessages }) => {
124
+ // Persist worker session with full tool call detail
125
+ for (const msg of responseMessages)
126
+ session.append(msg);
127
+ sessionStore.save(session).then(() => {
128
+ // Enrich worker session metadata for UI display
129
+ sessionStore.updateMetadata(workerSessionKey, {
130
+ channelType: "worker",
131
+ agentId,
132
+ agentName,
133
+ ...(teamName ? { teamName } : {}),
134
+ });
135
+ }).catch((err) => {
136
+ logger.warn(`[delegate] Failed to save worker session ${workerSessionKey}: ${err instanceof Error ? err.message : err}`);
137
+ });
138
+ // Close the worker's own browser instance
139
+ cleanupWorkerBrowser(workerBrowser, agentName);
140
+ channelStore.post(channelId, agentId, "result", result);
141
+ delegationStore.markCompleted(delegationId, result);
142
+ logger.info(`[delegate] ${agentName} completed (${result.length} chars, channel: ${channelId})`);
143
+ emit("agent", {
144
+ sessionKey,
145
+ delegation: { agentId, agentName, channelId, status: "completed", resultLength: result.length },
146
+ });
147
+ onWorkerComplete(sessionKey, channelId);
148
+ }, (err) => {
149
+ const error = err instanceof Error ? err.message : String(err);
150
+ // Persist worker session even on failure for debugging
151
+ sessionStore.save(session).then(() => {
152
+ sessionStore.updateMetadata(workerSessionKey, {
153
+ channelType: "worker",
154
+ agentId,
155
+ agentName,
156
+ ...(teamName ? { teamName } : {}),
157
+ });
158
+ }).catch((saveErr) => {
159
+ logger.warn(`[delegate] Failed to save worker session ${workerSessionKey}: ${saveErr instanceof Error ? saveErr.message : saveErr}`);
160
+ });
161
+ // Close the worker's own browser instance even on failure
162
+ cleanupWorkerBrowser(workerBrowser, agentName);
163
+ channelStore.post(channelId, agentId, "error", error);
164
+ delegationStore.markFailed(delegationId, error);
165
+ logger.error(`[delegate] ${agentName} failed: ${error} (channel: ${channelId})`);
166
+ emit("agent", {
167
+ sessionKey,
168
+ delegation: { agentId, agentName, channelId, status: "failed", error },
169
+ });
170
+ onWorkerComplete(sessionKey, channelId);
171
+ });
172
+ return `Delegated to ${agentName}. Channel: ${channelId}`;
173
+ },
174
+ });
175
+ const listWorkers = tool({
176
+ description: "List available worker names you can delegate tasks to.",
177
+ inputSchema: z.object({}),
178
+ execute: async () => {
179
+ if (delegatable.length === 0)
180
+ return "No workers available.";
181
+ return `Available workers:\n${delegatable.map((name) => `- ${name}`).join("\n")}`;
182
+ },
183
+ });
184
+ const workerGet = tool({
185
+ description: "Get full configuration details for one worker by name (identity, tools, model, limits, subscriptions).",
186
+ inputSchema: z.object({
187
+ worker: z.string().describe("Worker name to inspect"),
188
+ }),
189
+ execute: async ({ worker }) => {
190
+ const workerConfig = registry.getWorker(worker);
191
+ if (!workerConfig) {
192
+ return `Unknown worker: ${worker}. Call ${listWorkersToolName} to see available workers.`;
193
+ }
194
+ return formatWorkerDetails(workerConfig, workerGetToolName);
195
+ },
196
+ });
197
+ const readChannel = createReadChannelTool(channelStore, idToName);
198
+ return {
199
+ delegate,
200
+ list_workers: listWorkers,
201
+ worker_get: workerGet,
202
+ read_channel: readChannel,
203
+ };
204
+ }
205
+ /** Standalone read_channel tool — reused by both orchestrator and workers. */
206
+ function createReadChannelTool(channelStore, nameMap) {
207
+ return tool({
208
+ description: "Read all messages from a delegation channel. Returns the full conversation log " +
209
+ "including tasks, results, and errors posted by any agent.",
210
+ inputSchema: z.object({
211
+ channelId: z
212
+ .string()
213
+ .describe("The channel ID to read"),
214
+ }),
215
+ execute: async ({ channelId }) => {
216
+ const messages = channelStore.read(channelId);
217
+ if (messages.length === 0)
218
+ return `Channel ${channelId}: no messages yet.`;
219
+ return messages
220
+ .map((m) => `[${nameMap?.get(m.sender) ?? m.sender}] (${m.role}): ${m.content}`)
221
+ .join("\n\n");
222
+ },
223
+ });
224
+ }
225
+ const DEFAULT_WORKER_MAX_STEPS = 20;
226
+ /** Run a worker's loop — returned promise settles when done. */
227
+ async function runWorker(agentId, agentName, model, provider, modelId, codexReasoningEffort, system, session, tools, workerSessionKey, maxSteps, sessionLabel, sandboxEnabled = false) {
228
+ let cleanup;
229
+ try {
230
+ const adapted = await adaptTools(provider, modelId, model, tools, {
231
+ sandboxEnabled,
232
+ codexReasoningEffort,
233
+ });
234
+ cleanup = adapted.cleanup;
235
+ return await runLoop({
236
+ model: adapted.model,
237
+ system,
238
+ messages: session.getMessages(),
239
+ tools: adapted.tools,
240
+ sessionKey: workerSessionKey,
241
+ sessionLabel,
242
+ agentId,
243
+ maxSteps: maxSteps ?? DEFAULT_WORKER_MAX_STEPS,
244
+ silent: true,
245
+ });
246
+ }
247
+ catch (err) {
248
+ const error = err instanceof Error ? err.message : String(err);
249
+ logger.error(`[delegate] ${agentName} worker failed: ${error}`);
250
+ throw err;
251
+ }
252
+ finally {
253
+ if (cleanup) {
254
+ try {
255
+ await cleanup();
256
+ }
257
+ catch (err) {
258
+ logger.warn(`[delegate] ${agentName} worker MCP cleanup failed: ${err instanceof Error ? err.message : err}`);
259
+ }
260
+ }
261
+ }
262
+ }
263
+ /** Close the browser if the worker had it open. No-op if no browser or already closed. */
264
+ function cleanupWorkerBrowser(browserManager, agentName) {
265
+ if (!browserManager?.isLaunched())
266
+ return;
267
+ browserManager.close().catch((err) => {
268
+ logger.warn(`[delegate] ${agentName} browser cleanup failed: ${err instanceof Error ? err.message : err}`);
269
+ });
270
+ }
271
+ function formatWorkerDetails(worker, workerGetToolName) {
272
+ const toolsList = worker.tools.length > 0 ? worker.tools.join(", ") : "(all tools)";
273
+ const subscriptionsList = worker.subscriptions.length > 0 ? worker.subscriptions.join(", ") : "(none)";
274
+ const templateId = worker.templateId ?? "(none)";
275
+ const identity = worker.identity.trim() || "(empty)";
276
+ return [
277
+ `Worker: ${worker.name}`,
278
+ `- id: ${worker.id}`,
279
+ `- model: ${worker.model}`,
280
+ `- contextWindow: ${worker.contextWindow}`,
281
+ `- maxSteps: ${worker.maxSteps}`,
282
+ `- timeoutSeconds: ${worker.timeout}`,
283
+ `- concurrency: ${worker.concurrency}`,
284
+ `- subscriptions: ${subscriptionsList}`,
285
+ `- templateId: ${templateId}`,
286
+ `- tools: ${toolsList}`,
287
+ `- identity:`,
288
+ identity,
289
+ "",
290
+ `Tip: use ${workerGetToolName} with another name to inspect a different worker.`,
291
+ ].join("\n");
292
+ }
293
+ /** Race a promise against a timeout. */
294
+ function withTimeout(promise, ms, message) {
295
+ return new Promise((resolve, reject) => {
296
+ const timer = setTimeout(() => reject(new Error(message)), ms);
297
+ promise.then((val) => { clearTimeout(timer); resolve(val); }, (err) => { clearTimeout(timer); reject(err); });
298
+ });
299
+ }
@@ -0,0 +1,4 @@
1
+ import { type Tool } from "ai";
2
+ export declare function createFsTools(opts?: {
3
+ sandboxed?: boolean;
4
+ }): Record<string, Tool>;