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,84 @@
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) {
11
+ const lane = this.getOrCreateLane(sessionKey);
12
+ return new Promise((resolve, reject) => {
13
+ lane.queue.push({ text, 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?.abort)
24
+ return false;
25
+ lane.abort.abort();
26
+ return true;
27
+ }
28
+ /** Remove the lane for a cleared session. Aborts any in-flight run first. */
29
+ deleteLane(sessionKey) {
30
+ const lane = this.lanes.get(sessionKey);
31
+ if (lane?.abort)
32
+ lane.abort.abort();
33
+ this.lanes.delete(sessionKey);
34
+ }
35
+ async drain(sessionKey) {
36
+ const lane = this.lanes.get(sessionKey);
37
+ if (!lane || lane.running)
38
+ return;
39
+ lane.running = true;
40
+ while (lane.queue.length > 0) {
41
+ const batch = lane.queue.splice(0);
42
+ const controller = new AbortController();
43
+ lane.abort = controller;
44
+ try {
45
+ if (this.mode === "collect") {
46
+ const combined = batch.map((m) => m.text).join("\n");
47
+ const reply = await this.processMessage(sessionKey, combined, controller.signal);
48
+ // Only the last message gets the reply; earlier ones resolve empty (no Telegram send)
49
+ for (let i = 0; i < batch.length - 1; i++)
50
+ batch[i].resolve("");
51
+ batch[batch.length - 1].resolve(reply);
52
+ }
53
+ else {
54
+ for (const msg of batch) {
55
+ if (controller.signal.aborted) {
56
+ msg.reject(new Error("Aborted"));
57
+ continue;
58
+ }
59
+ const reply = await this.processMessage(sessionKey, msg.text, controller.signal);
60
+ msg.resolve(reply);
61
+ }
62
+ }
63
+ }
64
+ catch (err) {
65
+ for (const msg of batch)
66
+ msg.reject(err);
67
+ }
68
+ lane.abort = null;
69
+ // Loop continues if new messages arrived during processing
70
+ }
71
+ lane.running = false;
72
+ // Remove idle lane to prevent unbounded growth
73
+ if (lane.queue.length === 0)
74
+ this.lanes.delete(sessionKey);
75
+ }
76
+ getOrCreateLane(sessionKey) {
77
+ let lane = this.lanes.get(sessionKey);
78
+ if (!lane) {
79
+ lane = { queue: [], running: false, abort: null };
80
+ this.lanes.set(sessionKey, lane);
81
+ }
82
+ return lane;
83
+ }
84
+ }
@@ -0,0 +1,47 @@
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 { ChannelManager } from "../channels/manager.js";
18
+ /** All dependencies needed to build per-run tools. */
19
+ export interface RunToolsDeps {
20
+ baseTools: ToolSet;
21
+ config: Config;
22
+ memoryStore: MemoryStore | null;
23
+ embeddingProvider: EmbeddingProvider | null;
24
+ memoryMaxResults: number;
25
+ sandbox: DockerSandbox | null;
26
+ skillManager: SkillManager;
27
+ integrationRegistry: IntegrationRegistry;
28
+ scheduleStore: ScheduleStore | null;
29
+ taskStore: TaskStore | null;
30
+ teamStore: TeamStore | null;
31
+ desktopAdapter: DesktopAdapter | null;
32
+ browserManager: BrowserManager | null;
33
+ /** Effective model string ("provider:id") for the current run — may differ from global config when inside a team. */
34
+ effectiveModel: string;
35
+ agentRegistry: AgentRegistry | null;
36
+ delegationStore: DelegationStore | null;
37
+ channelStore: ChannelStore | null;
38
+ channelManager: ChannelManager | null;
39
+ sessionStore: SessionStore;
40
+ modelId: string;
41
+ onWorkerComplete: (sessionKey: string, channelId: string) => void;
42
+ }
43
+ /** Build the per-run tool set based on current session state. */
44
+ export declare function buildRunTools(deps: RunToolsDeps, sessionKey: string, activeIntegrations: Set<string>, channelInfo?: {
45
+ channelType: string;
46
+ channelId: string;
47
+ }, agentId?: string, teamId?: string, scheduleTeamId?: string, sessionLabel?: string): ToolSet;
@@ -0,0 +1,84 @@
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 { createSpeakTools } from "../tools/speak.js";
5
+ import { createDelegationTools } from "../tools/delegate.js";
6
+ import { createIntegrationToggleTools } from "../tools/integration-toggle.js";
7
+ import { createScheduleTools } from "../tools/schedule.js";
8
+ import { createTaskTools } from "../tools/tasks.js";
9
+ import { createTeamManagementTools } from "../tools/teams.js";
10
+ import { createChannelHistoryTool } from "../tools/channel-history.js";
11
+ import { DEFAULT_TEAM_ID } from "../config/agent-config.js";
12
+ /** Build the per-run tool set based on current session state. */
13
+ export function buildRunTools(deps, sessionKey, activeIntegrations, channelInfo, agentId, teamId, scheduleTeamId, sessionLabel) {
14
+ const tools = { ...deps.baseTools };
15
+ // Session-scoped bash tool (needs sessionKey for sandbox container routing)
16
+ const bashTool = createBashTool(deps.config.bash, {
17
+ sessionKey,
18
+ sandbox: deps.sandbox,
19
+ });
20
+ if (bashTool)
21
+ tools.bash = bashTool;
22
+ // When sandbox is enabled, remove fs tools — bash inside the container covers these
23
+ if (deps.sandbox) {
24
+ for (const name of ["read", "write", "edit", "grep", "find", "ls"]) {
25
+ delete tools[name];
26
+ }
27
+ }
28
+ if (deps.memoryStore) {
29
+ // Scope memory to team when talking to a non-default team
30
+ const memoryTeamId = teamId && teamId !== DEFAULT_TEAM_ID ? teamId : undefined;
31
+ tools.memory_search = createMemorySearchTool(deps.memoryStore, deps.embeddingProvider, deps.memoryMaxResults, memoryTeamId);
32
+ tools.memory_save = createMemorySaveTool(deps.memoryStore, deps.embeddingProvider, sessionKey, memoryTeamId);
33
+ }
34
+ // Desktop tool — rebuilt per-run to track model/config changes
35
+ if (deps.config.desktop.enabled && deps.desktopAdapter) {
36
+ tools.computer = createDesktopTool(deps.desktopAdapter, deps.modelId);
37
+ }
38
+ // TTS tools — rebuilt per-run so enabling/disabling takes effect immediately
39
+ const ttsTools = createSpeakTools(deps.config.tts);
40
+ if (ttsTools)
41
+ Object.assign(tools, ttsTools);
42
+ // Dynamic read_skill tool (reflects latest SkillManager state after hot-reload)
43
+ const readSkill = deps.skillManager.readTool;
44
+ if (readSkill)
45
+ tools.read_skill = readSkill;
46
+ // Integration tools (only for active integrations)
47
+ Object.assign(tools, deps.integrationRegistry.getToolsFor(activeIntegrations));
48
+ // Enable/disable integration tools (always present if integrations exist)
49
+ if (deps.integrationRegistry.names.length > 0) {
50
+ Object.assign(tools, createIntegrationToggleTools(deps.integrationRegistry, activeIntegrations));
51
+ }
52
+ // Delegation tools (only for orchestrators with workers)
53
+ if (agentId && deps.agentRegistry && deps.delegationStore && deps.channelStore) {
54
+ const workers = deps.agentRegistry.delegatableWorkers();
55
+ if (workers.length > 0) {
56
+ Object.assign(tools, createDelegationTools(deps.agentRegistry, deps.channelStore, deps.delegationStore, deps.sessionStore, agentId, sessionKey, deps.memoryStore, deps.embeddingProvider, deps.memoryMaxResults, deps.onWorkerComplete, deps.browserManager
57
+ ? { headless: deps.config.browserHeadless, userAgent: deps.config.browserUserAgent || undefined }
58
+ : null, sessionLabel));
59
+ }
60
+ }
61
+ // Task tools — scoped to team when talking to a specific team
62
+ if (deps.taskStore) {
63
+ Object.assign(tools, createTaskTools(deps.taskStore, teamId));
64
+ }
65
+ // Team management tools — only for the default team orchestrator
66
+ if (deps.teamStore && (!teamId || teamId === DEFAULT_TEAM_ID)) {
67
+ Object.assign(tools, createTeamManagementTools(deps.teamStore));
68
+ }
69
+ // Schedule management tools (team-scoped — uses separate scheduleTeamId to avoid default-team regression)
70
+ const effectiveScheduleTeamId = scheduleTeamId ?? teamId;
71
+ if (deps.scheduleStore && effectiveScheduleTeamId) {
72
+ Object.assign(tools, createScheduleTools(deps.scheduleStore, effectiveScheduleTeamId, deps.integrationRegistry.names));
73
+ }
74
+ // Channel history tool — lets the LLM read recent messages on demand.
75
+ // Uses a resolver callback so the channelId is read fresh at execution time,
76
+ // not captured in a stale closure at tool creation time.
77
+ if (channelInfo && deps.channelManager) {
78
+ const { channelType, channelId } = channelInfo;
79
+ const historyTool = createChannelHistoryTool(deps.channelManager, channelType, () => channelId);
80
+ if (historyTool)
81
+ tools.read_channel_history = historyTool;
82
+ }
83
+ return tools;
84
+ }
@@ -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
+ }
@@ -0,0 +1,50 @@
1
+ import { Session } from "./session.js";
2
+ export interface SessionIndexEntry {
3
+ key: string;
4
+ file: string;
5
+ messageCount: number;
6
+ updatedAt: number;
7
+ /** Human-readable title — auto-set from first user message, editable via rename. */
8
+ title?: string;
9
+ /** Team UUID (stable reference, survives renames). */
10
+ teamId?: string;
11
+ /** Team name snapshot at save time (denormalized for fast display). */
12
+ teamName?: string;
13
+ /** Channel type: "gateway", "worker", "scheduler", "telegram", etc. */
14
+ channelType?: string;
15
+ /** Worker agent ID (stable reference for worker sessions). */
16
+ agentId?: string;
17
+ /** Worker agent name snapshot at save time (denormalized for display). */
18
+ agentName?: string;
19
+ }
20
+ export declare class SessionStore {
21
+ private dataDir;
22
+ private indexPath;
23
+ private index;
24
+ constructor(dataDir: string);
25
+ /** Save session to JSONL. Three modes: compaction marker, full rewrite, or append. */
26
+ save(session: Session): Promise<void>;
27
+ /** Load a session from its JSONL file. Respects compaction markers. */
28
+ load(sessionKey: string): Promise<Session | null>;
29
+ /** List all session keys with metadata. */
30
+ list(): SessionIndexEntry[];
31
+ /** Rename a session (update its title in the index). */
32
+ rename(sessionKey: string, title: string): boolean;
33
+ /**
34
+ * Update session metadata (team name, agent info). Only writes to disk if something changed.
35
+ * Note: teamName and agentName are denormalized snapshots — they may become stale if
36
+ * the team or agent is renamed after the session was last active.
37
+ */
38
+ updateMetadata(sessionKey: string, meta: {
39
+ teamId?: string;
40
+ teamName?: string;
41
+ channelType?: string;
42
+ agentId?: string;
43
+ agentName?: string;
44
+ }): void;
45
+ /** Clear a session — deletes the JSONL file and removes the index entry. */
46
+ clear(sessionKey: string): Promise<void>;
47
+ private getOrCreateEntry;
48
+ private loadIndex;
49
+ private saveIndex;
50
+ }
@@ -0,0 +1,207 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { Session } from "./session.js";
4
+ import { parseSessionKey } from "./session-key.js";
5
+ const MAX_TITLE_LEN = 30;
6
+ function isCompaction(entry) {
7
+ return "type" in entry && entry.type === "compaction";
8
+ }
9
+ export class SessionStore {
10
+ dataDir;
11
+ indexPath;
12
+ index;
13
+ constructor(dataDir) {
14
+ this.dataDir = dataDir;
15
+ this.indexPath = path.join(dataDir, "index.json");
16
+ // Ensure directory exists
17
+ fs.mkdirSync(dataDir, { recursive: true });
18
+ // Load existing index
19
+ this.index = this.loadIndex();
20
+ }
21
+ /** Save session to JSONL. Three modes: compaction marker, full rewrite, or append. */
22
+ async save(session) {
23
+ const entry = this.getOrCreateEntry(session.key);
24
+ const filePath = path.join(this.dataDir, entry.file);
25
+ if (session.pendingCompaction) {
26
+ // Compaction: append marker + re-append kept messages (append-only, preserves history)
27
+ const marker = {
28
+ type: "compaction",
29
+ ts: Date.now(),
30
+ summary: session.pendingCompaction.summary,
31
+ };
32
+ const kept = session.getCompactionMessages();
33
+ const lines = JSON.stringify(marker) +
34
+ "\n" +
35
+ kept.map((msg) => JSON.stringify({ ts: Date.now(), message: msg })).join("\n") +
36
+ "\n";
37
+ fs.appendFileSync(filePath, lines, "utf-8");
38
+ }
39
+ else if (session.needsFullRewrite) {
40
+ // Emergency truncate: overwrite entire file
41
+ const all = session.getMessages();
42
+ const lines = all.map((msg) => JSON.stringify({ ts: Date.now(), message: msg })).join("\n") + "\n";
43
+ fs.writeFileSync(filePath, lines, "utf-8");
44
+ }
45
+ else {
46
+ // Normal: append only new messages
47
+ const newMessages = session.getNewMessages();
48
+ if (newMessages.length === 0)
49
+ return;
50
+ const lines = newMessages.map((msg) => JSON.stringify({ ts: Date.now(), message: msg })).join("\n") + "\n";
51
+ fs.appendFileSync(filePath, lines, "utf-8");
52
+ }
53
+ session.markPersisted();
54
+ // Update index
55
+ entry.messageCount = session.messageCount;
56
+ entry.updatedAt = Date.now();
57
+ // Auto-set title from first user message (once)
58
+ if (!entry.title) {
59
+ const firstUserMsg = session
60
+ .getMessages()
61
+ .find((m) => m.role === "user" && typeof m.content === "string" && m.content.trim());
62
+ if (firstUserMsg && typeof firstUserMsg.content === "string") {
63
+ const raw = firstUserMsg.content.trim().split("\n")[0];
64
+ entry.title = raw.length > MAX_TITLE_LEN ? raw.slice(0, MAX_TITLE_LEN - 1) + "\u2026" : raw;
65
+ }
66
+ }
67
+ this.saveIndex();
68
+ }
69
+ /** Load a session from its JSONL file. Respects compaction markers. */
70
+ async load(sessionKey) {
71
+ const entry = this.index.get(sessionKey);
72
+ if (!entry)
73
+ return null;
74
+ const filePath = path.join(this.dataDir, entry.file);
75
+ if (!fs.existsSync(filePath))
76
+ return null;
77
+ const content = fs.readFileSync(filePath, "utf-8");
78
+ const entries = [];
79
+ for (const line of content.split("\n")) {
80
+ if (!line.trim())
81
+ continue;
82
+ try {
83
+ entries.push(JSON.parse(line));
84
+ }
85
+ catch {
86
+ // skip malformed lines
87
+ }
88
+ }
89
+ // Find the last compaction marker
90
+ let lastCompactionIdx = -1;
91
+ for (let i = entries.length - 1; i >= 0; i--) {
92
+ if (isCompaction(entries[i])) {
93
+ lastCompactionIdx = i;
94
+ break;
95
+ }
96
+ }
97
+ const messages = [];
98
+ if (lastCompactionIdx >= 0) {
99
+ // Compacted: summary message + all message entries after the marker
100
+ const marker = entries[lastCompactionIdx];
101
+ messages.push({
102
+ role: "user",
103
+ content: `[Prior conversation summary]\n${marker.summary}`,
104
+ });
105
+ for (let i = lastCompactionIdx + 1; i < entries.length; i++) {
106
+ const e = entries[i];
107
+ if (!isCompaction(e)) {
108
+ messages.push(e.message);
109
+ }
110
+ }
111
+ }
112
+ else {
113
+ // No compaction: load all messages
114
+ for (const e of entries) {
115
+ if (!isCompaction(e)) {
116
+ messages.push(e.message);
117
+ }
118
+ }
119
+ }
120
+ return new Session(sessionKey, messages);
121
+ }
122
+ /** List all session keys with metadata. */
123
+ list() {
124
+ return [...this.index.values()].sort((a, b) => b.updatedAt - a.updatedAt);
125
+ }
126
+ /** Rename a session (update its title in the index). */
127
+ rename(sessionKey, title) {
128
+ if (typeof title !== "string")
129
+ return false;
130
+ const entry = this.index.get(sessionKey);
131
+ if (!entry)
132
+ return false;
133
+ // Strip control characters and trim
134
+ const sanitized = title.replace(/[\x00-\x1F\x7F-\x9F]/g, "").trim();
135
+ if (sanitized.length === 0)
136
+ return false;
137
+ entry.title = sanitized.length > MAX_TITLE_LEN ? sanitized.slice(0, MAX_TITLE_LEN - 1) + "\u2026" : sanitized;
138
+ this.saveIndex();
139
+ return true;
140
+ }
141
+ /**
142
+ * Update session metadata (team name, agent info). Only writes to disk if something changed.
143
+ * Note: teamName and agentName are denormalized snapshots — they may become stale if
144
+ * the team or agent is renamed after the session was last active.
145
+ */
146
+ updateMetadata(sessionKey, meta) {
147
+ const entry = this.index.get(sessionKey);
148
+ if (!entry)
149
+ return;
150
+ const FIELDS = ["teamId", "teamName", "channelType", "agentId", "agentName"];
151
+ let changed = false;
152
+ for (const field of FIELDS) {
153
+ if (meta[field] !== undefined && entry[field] !== meta[field]) {
154
+ entry[field] = meta[field];
155
+ changed = true;
156
+ }
157
+ }
158
+ if (changed)
159
+ this.saveIndex();
160
+ }
161
+ /** Clear a session — deletes the JSONL file and removes the index entry. */
162
+ async clear(sessionKey) {
163
+ const entry = this.index.get(sessionKey);
164
+ if (!entry)
165
+ return;
166
+ const filePath = path.join(this.dataDir, entry.file);
167
+ if (fs.existsSync(filePath)) {
168
+ fs.unlinkSync(filePath);
169
+ }
170
+ this.index.delete(sessionKey);
171
+ this.saveIndex();
172
+ }
173
+ // --- internal ---
174
+ getOrCreateEntry(key) {
175
+ let entry = this.index.get(key);
176
+ if (!entry) {
177
+ const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
178
+ const parsed = parseSessionKey(key);
179
+ entry = {
180
+ key,
181
+ file: `${safeKey}.jsonl`,
182
+ messageCount: 0,
183
+ updatedAt: Date.now(),
184
+ teamId: parsed.teamId,
185
+ channelType: parsed.channelType,
186
+ };
187
+ this.index.set(key, entry);
188
+ }
189
+ return entry;
190
+ }
191
+ loadIndex() {
192
+ if (!fs.existsSync(this.indexPath)) {
193
+ return new Map();
194
+ }
195
+ try {
196
+ const raw = JSON.parse(fs.readFileSync(this.indexPath, "utf-8"));
197
+ return new Map(Object.entries(raw));
198
+ }
199
+ catch {
200
+ return new Map();
201
+ }
202
+ }
203
+ saveIndex() {
204
+ const obj = Object.fromEntries(this.index);
205
+ fs.writeFileSync(this.indexPath, JSON.stringify(obj, null, 2), "utf-8");
206
+ }
207
+ }
@@ -0,0 +1,32 @@
1
+ import type { LanguageModel, ModelMessage } from "ai";
2
+ export interface PendingCompaction {
3
+ summary: string;
4
+ }
5
+ export declare class Session {
6
+ readonly key: string;
7
+ private messages;
8
+ private persistedCount;
9
+ private _pendingCompaction;
10
+ private _needsFullRewrite;
11
+ constructor(key: string, messages?: ModelMessage[]);
12
+ append(msg: ModelMessage): void;
13
+ getMessages(): ModelMessage[];
14
+ /** Returns only messages not yet written to disk (normal append path). */
15
+ getNewMessages(): ModelMessage[];
16
+ /** After compaction: returns kept + new messages (everything except the summary at index 0). */
17
+ getCompactionMessages(): ModelMessage[];
18
+ /** Mark all current messages as persisted. */
19
+ markPersisted(): void;
20
+ /** Pending compaction info for the store to persist as a marker. */
21
+ get pendingCompaction(): PendingCompaction | null;
22
+ /** True when the file must be fully overwritten (emergency truncate). */
23
+ get needsFullRewrite(): boolean;
24
+ /** LLM-based compaction — summarizes old messages when tokens exceed context budget. */
25
+ compact(model: LanguageModel, contextWindow: number, systemPrompt: string): Promise<boolean>;
26
+ /** Replace the internal messages array (e.g. after programmatic compaction). Marks full rewrite. */
27
+ replaceMessages(messages: ModelMessage[]): void;
28
+ /** Emergency truncation — drop oldest messages, keeping last N. */
29
+ truncate(keep: number): void;
30
+ get messageCount(): number;
31
+ get updatedAt(): number;
32
+ }