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,82 @@
1
+ import { randomUUID } from "crypto";
2
+ import { extractFacts } from "../memory/extractor.js";
3
+ import { deriveMemoryTeamId } from "./session-key.js";
4
+ import { logger } from "../logger.js";
5
+ /** Run fact extraction every N user messages (not every message). */
6
+ const EXTRACT_EVERY_N = 5;
7
+ /**
8
+ * Handles memory extraction from conversation sessions.
9
+ * Tracks per-session message counts and batches extraction.
10
+ */
11
+ export class MemoryExtractor {
12
+ model;
13
+ memoryStore;
14
+ embeddingProvider;
15
+ constructor(model, memoryStore, embeddingProvider) {
16
+ this.model = model;
17
+ this.memoryStore = memoryStore;
18
+ this.embeddingProvider = embeddingProvider;
19
+ }
20
+ /** Update the model used for extraction (e.g. after config reload). */
21
+ setModel(model) {
22
+ this.model = model;
23
+ }
24
+ /**
25
+ * Increment extraction counter and trigger extraction if threshold reached.
26
+ * Non-blocking: extraction runs in background.
27
+ */
28
+ trackAndMaybeExtract(sessionKey, sessions, messages, teamId) {
29
+ const state = sessions.get(sessionKey);
30
+ if (!state)
31
+ return;
32
+ const count = state.messagesSinceExtraction + 1;
33
+ state.messagesSinceExtraction = count;
34
+ if (count >= EXTRACT_EVERY_N) {
35
+ state.messagesSinceExtraction = 0;
36
+ const recent = messages.slice(-EXTRACT_EVERY_N * 2);
37
+ this.extractAndSaveFacts(sessionKey, recent, teamId).catch((err) => {
38
+ logger.warn(`Fact extraction failed: ${err instanceof Error ? err.message : err}`);
39
+ });
40
+ }
41
+ }
42
+ /** Extract and save facts from messages to memory store. */
43
+ async extractAndSaveFacts(sessionKey, messages, teamId) {
44
+ const facts = await extractFacts(this.model, messages);
45
+ if (facts.length === 0)
46
+ return;
47
+ // Embed all facts in parallel when a provider is available
48
+ const embeddings = this.embeddingProvider
49
+ ? await Promise.all(facts.map((f) => this.embeddingProvider.embed(f)))
50
+ : facts.map(() => undefined);
51
+ let saved = 0;
52
+ for (let i = 0; i < facts.length; i++) {
53
+ const wasSaved = this.memoryStore.save({
54
+ id: randomUUID(),
55
+ fact: facts[i],
56
+ source: sessionKey,
57
+ timestamp: Date.now(),
58
+ embedding: embeddings[i],
59
+ teamId,
60
+ });
61
+ if (wasSaved)
62
+ saved++;
63
+ }
64
+ if (saved > 0) {
65
+ logger.info(`[${sessionKey}] Saved ${saved} new memories (${facts.length - saved} duplicates skipped)`);
66
+ }
67
+ }
68
+ /** Extract remaining facts from all active sessions (call before shutdown). */
69
+ async flushAll(sessions) {
70
+ for (const [sessionKey, state] of sessions.entries()) {
71
+ if (state.messagesSinceExtraction > 0) {
72
+ try {
73
+ const flushTeamId = deriveMemoryTeamId(sessionKey);
74
+ await this.extractAndSaveFacts(sessionKey, state.session.getMessages(), flushTeamId);
75
+ }
76
+ catch (err) {
77
+ logger.warn(`Shutdown extraction failed for ${sessionKey}: ${err instanceof Error ? err.message : err}`);
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
@@ -0,0 +1,14 @@
1
+ import type { LanguageModel } from "ai";
2
+ import type { CodexReasoningEffort } from "../config/model-spec.js";
3
+ /** Provider identifier for Claude Code (used for MCP adapter detection). */
4
+ export declare const CLAUDE_CODE_PROVIDER = "claude-code";
5
+ /** Provider identifier for Codex CLI (used for MCP adapter detection). */
6
+ export declare const CODEX_CLI_PROVIDER = "codex-cli";
7
+ /** Force subscription auth by clearing API key from the SDK environment. */
8
+ export declare const CODEX_CLI_ENV: {
9
+ readonly OPENAI_API_KEY: "";
10
+ };
11
+ export interface GetModelOptions {
12
+ codexReasoningEffort?: CodexReasoningEffort;
13
+ }
14
+ export declare function getModel(provider: string, modelId: string, options?: GetModelOptions): LanguageModel;
@@ -0,0 +1,85 @@
1
+ import { anthropic } from "@ai-sdk/anthropic";
2
+ import { createClaudeCode } from "ai-sdk-provider-claude-code";
3
+ import { createCodexCli } from "ai-sdk-provider-codex-cli";
4
+ import { openai } from "@ai-sdk/openai";
5
+ import { google } from "@ai-sdk/google";
6
+ import { xai } from "@ai-sdk/xai";
7
+ import { mistral } from "@ai-sdk/mistral";
8
+ import { groq } from "@ai-sdk/groq";
9
+ import { togetherai } from "@ai-sdk/togetherai";
10
+ import { deepseek } from "@ai-sdk/deepseek";
11
+ import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
12
+ import { minimax } from "vercel-minimax-ai-provider";
13
+ import { zhipu } from "zhipu-ai-provider";
14
+ /** Provider identifier for Claude Code (used for MCP adapter detection). */
15
+ export const CLAUDE_CODE_PROVIDER = "claude-code";
16
+ /** Provider identifier for Codex CLI (used for MCP adapter detection). */
17
+ export const CODEX_CLI_PROVIDER = "codex-cli";
18
+ /** Force subscription auth by clearing API key from the SDK environment. */
19
+ export const CODEX_CLI_ENV = { OPENAI_API_KEY: "" };
20
+ /**
21
+ * Create Claude Code provider lazily. Reads CLAUDE_CODE_OAUTH_TOKEN from
22
+ * process.env which is kept fresh by reloadConfig() → injectSecretsIntoEnv()
23
+ * on every request.
24
+ */
25
+ function getClaudeCode() {
26
+ const token = process.env.CLAUDE_CODE_OAUTH_TOKEN;
27
+ return createClaudeCode({
28
+ defaultSettings: {
29
+ permissionMode: "bypassPermissions",
30
+ env: {
31
+ ANTHROPIC_API_KEY: undefined,
32
+ ...(token ? { CLAUDE_CODE_OAUTH_TOKEN: token } : {}),
33
+ },
34
+ },
35
+ });
36
+ }
37
+ /**
38
+ * Base Codex CLI provider (no tools). Tools are added per-session in mcp-adapter.ts.
39
+ * Explicitly bypass approvals and sandbox to match "bypass all permissions" behavior.
40
+ */
41
+ const codexCliBase = createCodexCli({
42
+ defaultSettings: {
43
+ dangerouslyBypassApprovalsAndSandbox: true,
44
+ env: CODEX_CLI_ENV,
45
+ },
46
+ });
47
+ const providers = {
48
+ anthropic: (id) => anthropic(id),
49
+ [CLAUDE_CODE_PROVIDER]: (id) => getClaudeCode()(id, { streamingInput: "always" }),
50
+ [CODEX_CLI_PROVIDER]: (id, options) => codexCliBase(id, options?.codexReasoningEffort
51
+ ? { reasoningEffort: options.codexReasoningEffort }
52
+ : undefined),
53
+ openai: (id) => openai(id),
54
+ google: (id) => google(id),
55
+ xai: (id) => xai(id),
56
+ mistral: (id) => mistral(id),
57
+ groq: (id) => groq(id),
58
+ togetherai: (id) => togetherai(id),
59
+ deepseek: (id) => deepseek(id),
60
+ minimax: (id) => minimax(id),
61
+ zhipu: (id) => zhipu(id),
62
+ openrouter: (id) => {
63
+ const provider = createOpenAICompatible({
64
+ name: "openrouter",
65
+ baseURL: "https://openrouter.ai/api/v1",
66
+ apiKey: process.env.OPENROUTER_API_KEY,
67
+ });
68
+ return provider(id);
69
+ },
70
+ ollama: (id) => {
71
+ const provider = createOpenAICompatible({
72
+ name: "ollama",
73
+ baseURL: process.env.OLLAMA_BASE_URL ?? "http://localhost:11434/v1",
74
+ apiKey: "ollama",
75
+ });
76
+ return provider(id);
77
+ },
78
+ };
79
+ export function getModel(provider, modelId, options) {
80
+ const factory = providers[provider];
81
+ if (!factory) {
82
+ throw new Error(`Unknown provider: ${provider}. Available: ${Object.keys(providers).join(", ")}`);
83
+ }
84
+ return factory(modelId, options);
85
+ }
@@ -0,0 +1,18 @@
1
+ export type QueueMode = "sequential" | "collect";
2
+ export declare class MessageQueue {
3
+ private lanes;
4
+ private mode;
5
+ private processMessage;
6
+ constructor(opts: {
7
+ mode?: QueueMode;
8
+ processMessage: (sessionKey: string, text: string, signal: AbortSignal, images?: string[]) => Promise<string>;
9
+ });
10
+ /** Enqueue a message. Returns a promise that resolves with the agent reply. */
11
+ enqueue(sessionKey: string, text: string, images?: string[]): Promise<string>;
12
+ /** Abort the current run for a session. */
13
+ abort(sessionKey: string): boolean;
14
+ /** Remove the lane for a cleared session. Aborts any in-flight run first. */
15
+ deleteLane(sessionKey: string): void;
16
+ private drain;
17
+ private getOrCreateLane;
18
+ }
@@ -0,0 +1,111 @@
1
+ export class MessageQueue {
2
+ lanes = new Map();
3
+ mode;
4
+ processMessage;
5
+ constructor(opts) {
6
+ this.mode = opts.mode ?? "collect";
7
+ this.processMessage = opts.processMessage;
8
+ }
9
+ /** Enqueue a message. Returns a promise that resolves with the agent reply. */
10
+ enqueue(sessionKey, text, images) {
11
+ const lane = this.getOrCreateLane(sessionKey);
12
+ return new Promise((resolve, reject) => {
13
+ lane.queue.push({ text, images, resolve, reject });
14
+ // If idle, start draining
15
+ if (!lane.running) {
16
+ this.drain(sessionKey);
17
+ }
18
+ });
19
+ }
20
+ /** Abort the current run for a session. */
21
+ abort(sessionKey) {
22
+ const lane = this.lanes.get(sessionKey);
23
+ if (!lane)
24
+ return false;
25
+ let aborted = false;
26
+ if (lane.abort) {
27
+ lane.abort.abort();
28
+ aborted = true;
29
+ }
30
+ if (lane.queue.length > 0) {
31
+ const pending = lane.queue.splice(0);
32
+ for (const msg of pending)
33
+ msg.resolve("");
34
+ aborted = true;
35
+ }
36
+ return aborted;
37
+ }
38
+ /** Remove the lane for a cleared session. Aborts any in-flight run first. */
39
+ deleteLane(sessionKey) {
40
+ const lane = this.lanes.get(sessionKey);
41
+ if (lane?.abort)
42
+ lane.abort.abort();
43
+ this.lanes.delete(sessionKey);
44
+ }
45
+ async drain(sessionKey) {
46
+ const lane = this.lanes.get(sessionKey);
47
+ if (!lane || lane.running)
48
+ return;
49
+ lane.running = true;
50
+ while (lane.queue.length > 0) {
51
+ const batch = lane.queue.splice(0);
52
+ const controller = new AbortController();
53
+ lane.abort = controller;
54
+ try {
55
+ if (this.mode === "collect") {
56
+ const combined = batch.map((m) => m.text).join("\n");
57
+ // Collect images from all messages in the batch
58
+ const allImages = batch.flatMap((m) => m.images ?? []);
59
+ const images = allImages.length > 0 ? allImages : undefined;
60
+ const reply = await this.processMessage(sessionKey, combined, controller.signal, images);
61
+ // Only the last message gets the reply; earlier ones resolve empty (no Telegram send)
62
+ for (let i = 0; i < batch.length - 1; i++)
63
+ batch[i].resolve("");
64
+ batch[batch.length - 1].resolve(reply);
65
+ }
66
+ else {
67
+ for (const msg of batch) {
68
+ if (controller.signal.aborted) {
69
+ msg.resolve("");
70
+ continue;
71
+ }
72
+ const reply = await this.processMessage(sessionKey, msg.text, controller.signal, msg.images);
73
+ msg.resolve(reply);
74
+ }
75
+ }
76
+ }
77
+ catch (err) {
78
+ if (controller.signal.aborted || isAbortLikeError(err)) {
79
+ for (const msg of batch)
80
+ msg.resolve("");
81
+ }
82
+ else {
83
+ for (const msg of batch)
84
+ msg.reject(err);
85
+ }
86
+ }
87
+ lane.abort = null;
88
+ // Loop continues if new messages arrived during processing
89
+ }
90
+ lane.running = false;
91
+ // Remove idle lane to prevent unbounded growth
92
+ if (lane.queue.length === 0)
93
+ this.lanes.delete(sessionKey);
94
+ }
95
+ getOrCreateLane(sessionKey) {
96
+ let lane = this.lanes.get(sessionKey);
97
+ if (!lane) {
98
+ lane = { queue: [], running: false, abort: null };
99
+ this.lanes.set(sessionKey, lane);
100
+ }
101
+ return lane;
102
+ }
103
+ }
104
+ function isAbortLikeError(err) {
105
+ if (!(err instanceof Error))
106
+ return false;
107
+ if (err.name === "AbortError")
108
+ return true;
109
+ const msg = err.message.toLowerCase();
110
+ return msg.includes("aborted") || msg.includes("abort");
111
+ }
@@ -0,0 +1,50 @@
1
+ import type { ToolSet } from "ai";
2
+ import type { Config } from "../config.js";
3
+ import type { MemoryStore } from "../memory/store.js";
4
+ import type { EmbeddingProvider } from "../memory/embedding.js";
5
+ import type { DockerSandbox } from "../security/docker-sandbox.js";
6
+ import type { SkillManager } from "../skills/loader.js";
7
+ import type { IntegrationRegistry } from "../integrations/registry.js";
8
+ import type { ScheduleStore } from "../scheduler/store.js";
9
+ import type { DesktopAdapter } from "../computer/desktop/adapter.js";
10
+ import type { BrowserManager } from "../computer/browser/manager.js";
11
+ import type { AgentRegistry } from "./agent-registry.js";
12
+ import type { ChannelStore } from "./channel-store.js";
13
+ import type { DelegationStore } from "./delegation-store.js";
14
+ import type { SessionStore } from "./session-store.js";
15
+ import type { TaskStore } from "../tasks/store.js";
16
+ import type { TeamStore } from "../teams/store.js";
17
+ import type { PromptTemplateStore } from "../prompt-templates/store.js";
18
+ import type { ChannelManager } from "../channels/manager.js";
19
+ export type RunAgentRole = "orchestrator" | "worker";
20
+ /** All dependencies needed to build per-run tools. */
21
+ export interface RunToolsDeps {
22
+ baseTools: ToolSet;
23
+ config: Config;
24
+ memoryStore: MemoryStore | null;
25
+ embeddingProvider: EmbeddingProvider | null;
26
+ memoryMaxResults: number;
27
+ sandbox: DockerSandbox | null;
28
+ skillManager: SkillManager;
29
+ integrationRegistry: IntegrationRegistry;
30
+ scheduleStore: ScheduleStore | null;
31
+ taskStore: TaskStore | null;
32
+ teamStore: TeamStore | null;
33
+ promptTemplateStore: PromptTemplateStore | null;
34
+ desktopAdapter: DesktopAdapter | null;
35
+ browserManager: BrowserManager | null;
36
+ /** Effective model string ("provider:id") for the current run — may differ from global config when inside a team. */
37
+ effectiveModel: string;
38
+ agentRegistry: AgentRegistry | null;
39
+ delegationStore: DelegationStore | null;
40
+ channelStore: ChannelStore | null;
41
+ channelManager: ChannelManager | null;
42
+ sessionStore: SessionStore;
43
+ modelId: string;
44
+ onWorkerComplete: (sessionKey: string, channelId: string) => void;
45
+ }
46
+ /** Build the per-run tool set based on current session state. */
47
+ export declare function buildRunTools(deps: RunToolsDeps, sessionKey: string, activeIntegrations: Set<string>, channelInfo?: {
48
+ channelType: string;
49
+ channelId: string;
50
+ }, agentId?: string, agentRole?: RunAgentRole, teamId?: string, scheduleTeamId?: string, sessionLabel?: string, workspaceCwd?: string, agentToolAllowlist?: string[]): ToolSet;
@@ -0,0 +1,136 @@
1
+ import { createBashTool } from "../tools/bash.js";
2
+ import { createMemorySearchTool, createMemorySaveTool } from "../tools/memory.js";
3
+ import { createDesktopTool } from "../computer/desktop/tools.js";
4
+ import { createBrowserTools } from "../computer/browser/tools.js";
5
+ import { createSpeakTools } from "../tools/speak.js";
6
+ import { createDelegationTools } from "../tools/delegate.js";
7
+ import { createIntegrationToggleTools } from "../tools/integration-toggle.js";
8
+ import { createScheduleTools } from "../tools/schedule.js";
9
+ import { createTaskTools } from "../tools/tasks.js";
10
+ import { createScopedWorkerManagementTools, createTeamManagementTools } from "../tools/teams.js";
11
+ import { createPromptTemplateTools } from "../tools/prompt-templates.js";
12
+ import { createChannelHistoryTool } from "../tools/channel-history.js";
13
+ import { DEFAULT_TEAM_ID } from "../config/agent-config.js";
14
+ import { logger } from "../logger.js";
15
+ /** Build the per-run tool set based on current session state. */
16
+ export function buildRunTools(deps, sessionKey, activeIntegrations, channelInfo, agentId, agentRole, teamId, scheduleTeamId, sessionLabel, workspaceCwd, agentToolAllowlist) {
17
+ const enforceWorkerAllowlist = agentRole === "worker" &&
18
+ Array.isArray(agentToolAllowlist) &&
19
+ agentToolAllowlist.length > 0;
20
+ const allowedToolNames = new Set(agentToolAllowlist ?? []);
21
+ const isAllowed = (toolName) => !enforceWorkerAllowlist || allowedToolNames.has(toolName);
22
+ const mergeAllowedTools = (extraTools) => {
23
+ for (const [toolName, toolDef] of Object.entries(extraTools)) {
24
+ if (!isAllowed(toolName))
25
+ continue;
26
+ tools[toolName] = toolDef;
27
+ }
28
+ };
29
+ const tools = { ...deps.baseTools };
30
+ // Rebuild browser tools per run so browser actions are bound to the current session key.
31
+ if (deps.browserManager) {
32
+ const browserSessionKey = deps.config.browserMode === "per-tab-per-session" ? sessionKey : undefined;
33
+ mergeAllowedTools(createBrowserTools(deps.browserManager, browserSessionKey));
34
+ if (browserSessionKey) {
35
+ logger.debug(`[${sessionLabel}] Browser tools bound to session key: ${browserSessionKey}`);
36
+ }
37
+ }
38
+ // Session-scoped bash tool (needs sessionKey for sandbox container routing)
39
+ const bashTool = createBashTool(deps.config.bash, {
40
+ sessionKey,
41
+ sandbox: deps.sandbox,
42
+ cwd: workspaceCwd,
43
+ });
44
+ if (bashTool && isAllowed("bash"))
45
+ tools.bash = bashTool;
46
+ // When sandbox is enabled, remove fs tools — bash inside the container covers these
47
+ if (deps.sandbox) {
48
+ for (const name of ["read", "write", "edit", "grep", "find", "ls"]) {
49
+ delete tools[name];
50
+ }
51
+ }
52
+ if (deps.memoryStore) {
53
+ // Scope memory to team when talking to a non-default team
54
+ const memoryTeamId = teamId && teamId !== DEFAULT_TEAM_ID ? teamId : undefined;
55
+ if (isAllowed("memory_search")) {
56
+ tools.memory_search = createMemorySearchTool(deps.memoryStore, deps.embeddingProvider, deps.memoryMaxResults, memoryTeamId);
57
+ }
58
+ if (isAllowed("memory_save")) {
59
+ tools.memory_save = createMemorySaveTool(deps.memoryStore, deps.embeddingProvider, sessionKey, memoryTeamId);
60
+ }
61
+ }
62
+ // Desktop tool — rebuilt per-run to track model/config changes
63
+ if (deps.config.desktop.enabled && deps.desktopAdapter) {
64
+ if (isAllowed("computer")) {
65
+ tools.computer = createDesktopTool(deps.desktopAdapter, deps.modelId);
66
+ }
67
+ }
68
+ // TTS tools — rebuilt per-run so enabling/disabling takes effect immediately
69
+ const ttsTools = createSpeakTools(deps.config.tts);
70
+ if (ttsTools)
71
+ mergeAllowedTools(ttsTools);
72
+ // Dynamic read_skill tool (reflects latest SkillManager state after hot-reload)
73
+ const readSkill = deps.skillManager.readTool;
74
+ if (readSkill && isAllowed("read_skill"))
75
+ tools.read_skill = readSkill;
76
+ // Integration tools (only for active integrations)
77
+ mergeAllowedTools(deps.integrationRegistry.getToolsFor(activeIntegrations));
78
+ // Enable/disable integration tools (always present if integrations exist)
79
+ if (deps.integrationRegistry.names.length > 0) {
80
+ mergeAllowedTools(createIntegrationToggleTools(deps.integrationRegistry, activeIntegrations));
81
+ }
82
+ // Delegation tools (only for orchestrators with workers)
83
+ if (agentRole === "orchestrator" && agentId && deps.agentRegistry && deps.delegationStore && deps.channelStore) {
84
+ const workers = deps.agentRegistry.delegatableWorkers();
85
+ if (workers.length > 0) {
86
+ Object.assign(tools, createDelegationTools(deps.agentRegistry, deps.channelStore, deps.delegationStore, deps.sessionStore, agentId, sessionKey, deps.memoryStore, deps.embeddingProvider, deps.memoryMaxResults, deps.onWorkerComplete, deps.browserManager
87
+ ? {
88
+ headless: deps.config.browserHeadless,
89
+ userAgent: deps.config.browserUserAgent || undefined,
90
+ mode: deps.config.browserMode,
91
+ modeOptions: deps.config.browserModeOptions,
92
+ }
93
+ : null, sessionLabel, !!deps.sandbox));
94
+ }
95
+ }
96
+ // Task tools — scoped to team when talking to a specific team, with team's custom statuses
97
+ if (deps.taskStore) {
98
+ const teamStatuses = teamId && deps.teamStore
99
+ ? deps.teamStore.getTeamById(teamId)?.statuses ?? undefined
100
+ : undefined;
101
+ mergeAllowedTools(createTaskTools(deps.taskStore, teamId, teamStatuses, {
102
+ updatedBy: agentId ?? "assistant",
103
+ }));
104
+ }
105
+ // Team management tools:
106
+ // - default runtime: full team management
107
+ // - non-default orchestrator: worker CRUD scoped to its own team
108
+ const isDefaultRuntimeTeam = !teamId || teamId === DEFAULT_TEAM_ID;
109
+ if (deps.teamStore) {
110
+ if (isDefaultRuntimeTeam) {
111
+ mergeAllowedTools(createTeamManagementTools(deps.teamStore, DEFAULT_TEAM_ID, deps.promptTemplateStore ?? undefined));
112
+ }
113
+ else if (agentRole === "orchestrator") {
114
+ mergeAllowedTools(createScopedWorkerManagementTools(deps.teamStore, teamId, deps.promptTemplateStore ?? undefined));
115
+ }
116
+ }
117
+ // Prompt template tools stay on the default runtime only.
118
+ if (isDefaultRuntimeTeam && deps.promptTemplateStore) {
119
+ mergeAllowedTools(createPromptTemplateTools(deps.promptTemplateStore));
120
+ }
121
+ // Schedule management tools (team-scoped — uses separate scheduleTeamId to avoid default-team regression)
122
+ const effectiveScheduleTeamId = scheduleTeamId ?? teamId;
123
+ if (deps.scheduleStore && effectiveScheduleTeamId) {
124
+ mergeAllowedTools(createScheduleTools(deps.scheduleStore, effectiveScheduleTeamId, deps.integrationRegistry.names));
125
+ }
126
+ // Channel history tool — lets the LLM read recent messages on demand.
127
+ // Uses a resolver callback so the channelId is read fresh at execution time,
128
+ // not captured in a stale closure at tool creation time.
129
+ if (channelInfo && deps.channelManager) {
130
+ const { channelType, channelId } = channelInfo;
131
+ const historyTool = createChannelHistoryTool(deps.channelManager, channelType, () => channelId);
132
+ if (historyTool && isAllowed("read_channel_history"))
133
+ tools.read_channel_history = historyTool;
134
+ }
135
+ return tools;
136
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Build a session key: `{teamId}:{channelType}:{channelId}`.
3
+ */
4
+ export declare function buildSessionKey(teamId: string, channelType: string, channelId: string): string;
5
+ /**
6
+ * Extract a non-default teamId from a session key for memory scoping.
7
+ * Returns undefined for the default team (global memory).
8
+ */
9
+ export declare function deriveMemoryTeamId(sessionKey: string): string | undefined;
10
+ export interface ParsedSessionKey {
11
+ teamId?: string;
12
+ channelType?: string;
13
+ /** Agent name (for worker sessions only, extracted from key). */
14
+ agentName?: string;
15
+ isWorker: boolean;
16
+ }
17
+ /**
18
+ * Parse a session key into its components.
19
+ *
20
+ * Standard key: `{teamId}:{channelType}:{channelId}`
21
+ * Worker key: `{teamId}:{parentChannel}:{channelId}:{agentName}:{timestamp}`
22
+ */
23
+ export declare function parseSessionKey(key: string): ParsedSessionKey;
@@ -0,0 +1,41 @@
1
+ import { DEFAULT_TEAM_ID } from "../config/agent-config.js";
2
+ /**
3
+ * Build a session key: `{teamId}:{channelType}:{channelId}`.
4
+ */
5
+ export function buildSessionKey(teamId, channelType, channelId) {
6
+ return `${teamId}:${channelType}:${channelId}`;
7
+ }
8
+ /**
9
+ * Extract a non-default teamId from a session key for memory scoping.
10
+ * Returns undefined for the default team (global memory).
11
+ */
12
+ export function deriveMemoryTeamId(sessionKey) {
13
+ const { teamId } = parseSessionKey(sessionKey);
14
+ return teamId && teamId !== DEFAULT_TEAM_ID ? teamId : undefined;
15
+ }
16
+ /**
17
+ * Parse a session key into its components.
18
+ *
19
+ * Standard key: `{teamId}:{channelType}:{channelId}`
20
+ * Worker key: `{teamId}:{parentChannel}:{channelId}:{agentName}:{timestamp}`
21
+ */
22
+ export function parseSessionKey(key) {
23
+ const parts = key.split(":");
24
+ // Worker keys have 5+ parts with a numeric timestamp at the end
25
+ if (parts.length >= 5 && /^\d+$/.test(parts[parts.length - 1])) {
26
+ return {
27
+ isWorker: true,
28
+ teamId: parts[0],
29
+ channelType: "worker",
30
+ agentName: parts[parts.length - 2],
31
+ };
32
+ }
33
+ if (parts.length >= 3) {
34
+ return {
35
+ isWorker: false,
36
+ teamId: parts[0],
37
+ channelType: parts[1],
38
+ };
39
+ }
40
+ return { isWorker: false };
41
+ }
@@ -0,0 +1,36 @@
1
+ import type { Session } from "./session.js";
2
+ /** Consolidated per-session state (replaces 8 separate Maps). */
3
+ export interface SessionState {
4
+ session: Session;
5
+ agentId?: string;
6
+ teamId?: string;
7
+ channelType?: string;
8
+ channelId?: string;
9
+ integrations?: Set<string>;
10
+ messagesSinceExtraction: number;
11
+ delegationDepth: number;
12
+ /** Running estimate of message tokens (chars / 4). Updated on append, reset on compaction. */
13
+ estimatedMsgTokens: number;
14
+ replyCallback?: (reply: string) => Promise<void>;
15
+ workerTriggerTimer?: ReturnType<typeof setTimeout>;
16
+ workerPendingChannels?: Set<string>;
17
+ /** Integrations for the next scheduled task run (consumed once by main()). */
18
+ scheduledIntegrations?: string[];
19
+ }
20
+ /**
21
+ * Type-safe wrapper around a Map<string, SessionState>.
22
+ * Provides convenient accessors and cleanup helpers.
23
+ */
24
+ export declare class SessionStateMap {
25
+ private map;
26
+ get(key: string): SessionState | undefined;
27
+ /** Get existing state or create a new entry with the given session. */
28
+ getOrCreate(key: string, session: Session): SessionState;
29
+ /** Set a session state (used when loading from store). */
30
+ set(key: string, state: SessionState): void;
31
+ has(key: string): boolean;
32
+ delete(key: string): void;
33
+ entries(): IterableIterator<[string, SessionState]>;
34
+ /** Cancel all pending debounce timers (e.g. on shutdown). */
35
+ clearAllTimers(): void;
36
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Type-safe wrapper around a Map<string, SessionState>.
3
+ * Provides convenient accessors and cleanup helpers.
4
+ */
5
+ export class SessionStateMap {
6
+ map = new Map();
7
+ get(key) {
8
+ return this.map.get(key);
9
+ }
10
+ /** Get existing state or create a new entry with the given session. */
11
+ getOrCreate(key, session) {
12
+ let state = this.map.get(key);
13
+ if (!state) {
14
+ state = {
15
+ session,
16
+ messagesSinceExtraction: 0,
17
+ delegationDepth: 0,
18
+ estimatedMsgTokens: 0,
19
+ };
20
+ this.map.set(key, state);
21
+ }
22
+ return state;
23
+ }
24
+ /** Set a session state (used when loading from store). */
25
+ set(key, state) {
26
+ this.map.set(key, state);
27
+ }
28
+ has(key) {
29
+ return this.map.has(key);
30
+ }
31
+ delete(key) {
32
+ const state = this.map.get(key);
33
+ if (state?.workerTriggerTimer) {
34
+ clearTimeout(state.workerTriggerTimer);
35
+ }
36
+ this.map.delete(key);
37
+ }
38
+ entries() {
39
+ return this.map.entries();
40
+ }
41
+ /** Cancel all pending debounce timers (e.g. on shutdown). */
42
+ clearAllTimers() {
43
+ for (const state of this.map.values()) {
44
+ if (state.workerTriggerTimer) {
45
+ clearTimeout(state.workerTriggerTimer);
46
+ state.workerTriggerTimer = undefined;
47
+ }
48
+ state.workerPendingChannels = undefined;
49
+ }
50
+ }
51
+ }