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
package/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # VeryBot - Assistant for Your Work
2
+
3
+ <p align="center">
4
+ <strong>One assistant for your work across chat, tasks, teams, playbooks, and channels.</strong>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://opensource.org/license/mit"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge" alt="MIT License"></a>
9
+ <a href="https://github.com/charlie0077/verybot"><img src="https://img.shields.io/github/stars/charlie0077/verybot?style=for-the-badge" alt="GitHub stars"></a>
10
+ </p>
11
+
12
+ VeryBot is a self-hosted AI assistant platform for real work.
13
+ It helps individuals, teams, and developers run work from one place: chat with your assistant, organize tasks, manage teams, save playbooks, automate recurring jobs, and connect external channels like Telegram, Discord, Slack, and WhatsApp.
14
+
15
+ [Quick Start (User)](#quick-start-user) · [Quick Start (Developer)](#quick-start-developer) · [Let LLM Set Up Your Flow Example](#let-llm-set-up-your-flow-example) · [Playbooks](#playbooks) · [Docs](#docs)
16
+
17
+ ## Why VeryBot
18
+
19
+ - Reduce context-switching: one assistant across chat, tasks, teams, and channels.
20
+ - Turn talk into execution: convert conversations into structured tasks you can track and ship.
21
+ - Move faster with less micromanagement: set goals once and let the assistant create/follow up on work.
22
+ - Replace repetitive reminders with scheduled automation.
23
+ - Stay in control anywhere: use the same Control UI from desktop or mobile when reachable.
24
+ - Reuse what works: save proven workflows as playbooks and run them again.
25
+ - Keep your data ownership: self-host with local-first runtime data under `~/.verybot`.
26
+ - Extend only when needed: add tools, skills, and MCP integrations without changing core workflow.
27
+
28
+ ## Who It Is For
29
+
30
+ - Solo operators: personal work copilot with task and reminder automation.
31
+ - Small teams: shared team context, team-specific workflows, and worker agents.
32
+ - Developers: self-hosted runtime with tools, APIs, and extensibility.
33
+
34
+ ## Quick Start (User)
35
+
36
+ Requirements:
37
+
38
+ - Node.js `>=22`
39
+ - npm
40
+
41
+ Install and run (npm, fastest):
42
+
43
+ ```bash
44
+ npm install -g verybot
45
+ verybot
46
+ ```
47
+
48
+ Upgrade later:
49
+
50
+ ```bash
51
+ npm install -g verybot@latest
52
+ ```
53
+
54
+ Open the Control UI:
55
+
56
+ - `http://localhost:28789`
57
+
58
+ If the UI asks for a gateway token, you can:
59
+
60
+ - Copy it from the terminal output where you started `verybot` (`GATEWAY_TOKEN: ...`)
61
+ - Or run:
62
+
63
+ ```bash
64
+ verybot config get GATEWAY_TOKEN
65
+ ```
66
+
67
+ First setup in **Settings -> Agent**:
68
+
69
+ 1. Choose a model provider.
70
+ 2. If you picked an API-key provider, add your API key.
71
+ 3. If you use Codex CLI or Claude CLI and it already works on this machine, no API key setup is needed.
72
+ 4. Save and send a chat prompt like: `Set up a team and starter tasks for launch planning.`
73
+
74
+ Need to access it from LAN or internet?
75
+
76
+ ### LAN access
77
+
78
+ From installed CLI runtime (bind gateway/UI on all interfaces):
79
+
80
+ ```bash
81
+ verybot --host 0.0.0.0 --port 28789
82
+ ```
83
+
84
+ Then open:
85
+
86
+ - `http://<your-lan-ip>:28789`
87
+
88
+ ### Internet access (recommended setup)
89
+
90
+ 1. Run VeryBot on the server:
91
+
92
+ ```bash
93
+ verybot --host 127.0.0.1 --port 28789
94
+ ```
95
+
96
+ 2. Put a reverse proxy with TLS in front (example: Caddy):
97
+
98
+ ```caddy
99
+ verybot.example.com {
100
+ reverse_proxy 127.0.0.1:28789
101
+ }
102
+ ```
103
+
104
+ 3. Point your domain DNS to that server and open only ports `80/443` in firewall/security groups.
105
+ 4. Keep `GATEWAY_TOKEN` private and rotate it if leaked.
106
+
107
+ ## Quick Start (Developer)
108
+
109
+ Clone and run from source:
110
+
111
+ ```bash
112
+ git clone https://github.com/charlie0077/verybot.git
113
+ cd verybot
114
+ npm install
115
+ npm run dev
116
+ ```
117
+
118
+ Useful commands:
119
+
120
+ | Command | Purpose |
121
+ | --- | --- |
122
+ | `npm run dev` | Backend + UI dev servers |
123
+ | `npm run dev:backend` | Backend only (`tsx watch`) |
124
+ | `npm run dev:lan` | Expose UI on LAN (`0.0.0.0:10000`) |
125
+ | `npm run build` | Build backend + UI to `dist/` |
126
+ | `npm start` | Run production build |
127
+ | `npm test` | Run test suite |
128
+
129
+ ## Let LLM Set Up Your Flow Example
130
+
131
+ In **Chat**, you can ask the LLM to set up your workflow instead of doing everything manually.
132
+
133
+ Example prompts:
134
+
135
+ - `Create a team called Growth Ops for launch planning.`
136
+ - `For Growth Ops, set up task statuses: backlog, todo, in_progress, blocked, done.`
137
+ - `Create tasks for launch planning and place them in backlog.`
138
+ - `Set up a recurring Monday reminder to review launch risks.`
139
+ - `Create a playbook called Launch Checklist with clear handoff steps.`
140
+
141
+ Then review and refine in **Teams**, **Tasks**, **Scheduler**, and **Playbooks**.
142
+
143
+ ## Playbooks
144
+
145
+ Use **Playbooks** to store reusable workflows (SOPs, checklists, scripts) your assistant can follow.
146
+
147
+ Common playbooks:
148
+
149
+ - New customer onboarding
150
+ - Weekly team planning
151
+ - Incident triage
152
+ - Release readiness checklist
153
+
154
+ ## Channels (Optional)
155
+
156
+ Connect external channels in **Settings** -> **Channels**.
157
+
158
+ | Channel | Required keys |
159
+ | --- | --- |
160
+ | Telegram | `TELEGRAM_BOT_TOKEN` |
161
+ | Discord | `DISCORD_BOT_TOKEN` |
162
+ | Slack | `SLACK_BOT_TOKEN`, `SLACK_APP_TOKEN` |
163
+ | WhatsApp | `WHATSAPP_PHONE_ID` |
164
+
165
+ ## License
166
+
167
+ MIT
@@ -0,0 +1,21 @@
1
+ import type { CommandAlias } from "./types.js";
2
+ /** Max allowed alias length (including leading "/"). */
3
+ export declare const MAX_ALIAS_LENGTH = 64;
4
+ /** Max allowed alias expansion length. */
5
+ export declare const MAX_ALIAS_EXPANSION_LENGTH = 2000;
6
+ /** Global file-backed command alias storage. */
7
+ export declare class CommandAliasStore {
8
+ private readonly filePath;
9
+ private aliases;
10
+ private lastMtime;
11
+ private constructor();
12
+ static create(filePath: string): Promise<CommandAliasStore>;
13
+ list(): CommandAlias[];
14
+ upsert(rawAlias: string, rawExpansion: string): CommandAlias;
15
+ delete(rawAlias: string): boolean;
16
+ close(): void;
17
+ private loadFromFile;
18
+ private saveToFile;
19
+ private fileMtime;
20
+ private reloadIfChanged;
21
+ }
@@ -0,0 +1,148 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "fs";
2
+ import { randomUUID } from "crypto";
3
+ import { dirname, join } from "path";
4
+ import { logger } from "../logger.js";
5
+ /** Max allowed alias length (including leading "/"). */
6
+ export const MAX_ALIAS_LENGTH = 64;
7
+ /** Max allowed alias expansion length. */
8
+ export const MAX_ALIAS_EXPANSION_LENGTH = 2_000;
9
+ /** Slash command alias format: "/name" without whitespace. */
10
+ const ALIAS_RE = /^\/\S+$/;
11
+ function normalizeAlias(rawAlias) {
12
+ const trimmed = rawAlias.trim().toLowerCase();
13
+ if (!trimmed)
14
+ return "";
15
+ return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
16
+ }
17
+ function validateAlias(rawAlias) {
18
+ const alias = normalizeAlias(rawAlias);
19
+ if (!alias)
20
+ throw new Error("alias is required");
21
+ if (alias.length > MAX_ALIAS_LENGTH) {
22
+ throw new Error(`alias exceeds maximum length of ${MAX_ALIAS_LENGTH}`);
23
+ }
24
+ if (!ALIAS_RE.test(alias)) {
25
+ throw new Error("alias must be a single token with no spaces");
26
+ }
27
+ return alias;
28
+ }
29
+ function validateExpansion(expansion) {
30
+ const trimmed = expansion.trim();
31
+ if (!trimmed)
32
+ throw new Error("expansion is required");
33
+ if (trimmed.length > MAX_ALIAS_EXPANSION_LENGTH) {
34
+ throw new Error(`expansion exceeds maximum length of ${MAX_ALIAS_EXPANSION_LENGTH}`);
35
+ }
36
+ return trimmed;
37
+ }
38
+ function sortByAlias(a, b) {
39
+ return a.alias.localeCompare(b.alias);
40
+ }
41
+ /** Global file-backed command alias storage. */
42
+ export class CommandAliasStore {
43
+ filePath;
44
+ aliases = new Map();
45
+ lastMtime = null;
46
+ constructor(filePath) {
47
+ this.filePath = filePath;
48
+ this.aliases = this.loadFromFile();
49
+ this.lastMtime = this.fileMtime();
50
+ }
51
+ static async create(filePath) {
52
+ mkdirSync(dirname(filePath), { recursive: true });
53
+ return new CommandAliasStore(filePath);
54
+ }
55
+ list() {
56
+ this.reloadIfChanged();
57
+ return [...this.aliases.values()]
58
+ .sort(sortByAlias)
59
+ .map((row) => ({ ...row }));
60
+ }
61
+ upsert(rawAlias, rawExpansion) {
62
+ this.reloadIfChanged();
63
+ const alias = validateAlias(rawAlias);
64
+ const expansion = validateExpansion(rawExpansion);
65
+ const existing = this.aliases.get(alias);
66
+ const now = Date.now();
67
+ const nextAlias = {
68
+ alias,
69
+ expansion,
70
+ createdAt: existing?.createdAt ?? now,
71
+ updatedAt: now,
72
+ };
73
+ this.aliases.set(alias, nextAlias);
74
+ this.saveToFile();
75
+ return { ...nextAlias };
76
+ }
77
+ delete(rawAlias) {
78
+ this.reloadIfChanged();
79
+ const alias = validateAlias(rawAlias);
80
+ const existed = this.aliases.delete(alias);
81
+ if (existed)
82
+ this.saveToFile();
83
+ return existed;
84
+ }
85
+ close() {
86
+ // no-op: file-backed store
87
+ logger.info("Command alias store closed");
88
+ }
89
+ loadFromFile() {
90
+ if (!existsSync(this.filePath))
91
+ return new Map();
92
+ try {
93
+ const raw = readFileSync(this.filePath, "utf-8");
94
+ const parsed = JSON.parse(raw);
95
+ const rows = Array.isArray(parsed.aliases) ? parsed.aliases : [];
96
+ const map = new Map();
97
+ for (const row of rows) {
98
+ if (!row || typeof row !== "object")
99
+ continue;
100
+ if (typeof row.alias !== "string" || typeof row.expansion !== "string")
101
+ continue;
102
+ try {
103
+ const alias = validateAlias(row.alias);
104
+ const expansion = validateExpansion(row.expansion);
105
+ const createdAt = Number.isFinite(row.createdAt) ? Number(row.createdAt) : Date.now();
106
+ const updatedAt = Number.isFinite(row.updatedAt) ? Number(row.updatedAt) : createdAt;
107
+ map.set(alias, { alias, expansion, createdAt, updatedAt });
108
+ }
109
+ catch {
110
+ // Skip malformed alias rows.
111
+ }
112
+ }
113
+ return map;
114
+ }
115
+ catch (err) {
116
+ logger.warn(`Failed to read command alias file: ${err instanceof Error ? err.message : err}`);
117
+ return new Map();
118
+ }
119
+ }
120
+ saveToFile() {
121
+ const payload = {
122
+ aliases: [...this.aliases.values()]
123
+ .sort(sortByAlias)
124
+ .map((row) => ({ ...row })),
125
+ };
126
+ const dir = dirname(this.filePath);
127
+ mkdirSync(dir, { recursive: true });
128
+ const tmpPath = join(dir, `.command-aliases.${randomUUID()}.tmp`);
129
+ writeFileSync(tmpPath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
130
+ renameSync(tmpPath, this.filePath);
131
+ this.lastMtime = this.fileMtime();
132
+ }
133
+ fileMtime() {
134
+ try {
135
+ return statSync(this.filePath).mtimeMs;
136
+ }
137
+ catch {
138
+ return null;
139
+ }
140
+ }
141
+ reloadIfChanged() {
142
+ const mtime = this.fileMtime();
143
+ if (mtime === this.lastMtime)
144
+ return;
145
+ this.aliases = this.loadFromFile();
146
+ this.lastMtime = mtime;
147
+ }
148
+ }
@@ -0,0 +1,6 @@
1
+ export interface CommandAlias {
2
+ alias: string;
3
+ expansion: string;
4
+ createdAt: number;
5
+ updatedAt: number;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,96 @@
1
+ import type { LanguageModel, ToolSet } from "ai";
2
+ import type { AgentConfig, TeamConfig, TaskStatusConfig } from "../config/agent-config.js";
3
+ import type { Config } from "../config.js";
4
+ import type { MemoryStore } from "../memory/store.js";
5
+ import type { EmbeddingProvider } from "../memory/embedding.js";
6
+ import type { ModelDef } from "../config/model-catalog.js";
7
+ import type { CodexReasoningEffort } from "../config/model-spec.js";
8
+ export interface WorkerDeps {
9
+ memoryStore: MemoryStore | null;
10
+ embeddingProvider: EmbeddingProvider | null;
11
+ baseTools: ToolSet;
12
+ config: Config;
13
+ }
14
+ export interface ResolvedAgent {
15
+ agentConfig: AgentConfig;
16
+ model: LanguageModel;
17
+ modelDef: ModelDef;
18
+ tools: ToolSet;
19
+ }
20
+ export interface ResolvedAgentById {
21
+ role: "orchestrator" | "worker";
22
+ resolved: ResolvedAgent;
23
+ }
24
+ /**
25
+ * Holds agent configs for a single team.
26
+ * The first config is the orchestrator, rest are workers.
27
+ * `canDelegate` is auto-derived from team membership.
28
+ */
29
+ export declare class AgentRegistry {
30
+ private deps;
31
+ private orchestratorConfig;
32
+ /** Workers keyed by name (human-readable, unique within team). */
33
+ private workersByName;
34
+ private workerNames;
35
+ constructor(orchestrator: AgentConfig, workers: AgentConfig[], deps: WorkerDeps);
36
+ getOrchestrator(): AgentConfig;
37
+ /** Resolve the orchestrator into a model + def, ready for per-session override. */
38
+ resolveOrchestrator(): ResolvedAgent;
39
+ getWorker(name: string): AgentConfig | undefined;
40
+ /** Return the list of worker names that the orchestrator can delegate to. */
41
+ delegatableWorkers(): string[];
42
+ /** Build an id→name map for all agents (orchestrator + workers). */
43
+ buildIdToNameMap(): Map<string, string>;
44
+ /** Resolve a worker by name into a model + filtered tool set, ready for runLoop(). */
45
+ resolveWorker(name: string): ResolvedAgent | null;
46
+ /** Resolve any team agent by stable ID (orchestrator or worker). */
47
+ resolveAgentById(agentId: string): ResolvedAgentById | null;
48
+ private resolveConfig;
49
+ }
50
+ export interface TeamAgentInfo {
51
+ id: string;
52
+ name: string;
53
+ subscriptions: string[];
54
+ concurrency: number;
55
+ }
56
+ /** Info returned by TeamRegistry.listTeams() for UI display. */
57
+ export interface TeamInfo {
58
+ id: string;
59
+ name: string;
60
+ color: string;
61
+ orchestratorId: string;
62
+ orchestratorIdentity: string;
63
+ orchestratorModel: string;
64
+ workerCount: number;
65
+ /** Worker agents with subscription info for the task board UI. */
66
+ workers?: TeamAgentInfo[];
67
+ /** Custom task statuses (undefined = defaults). */
68
+ statuses?: TaskStatusConfig[];
69
+ }
70
+ /**
71
+ * Wraps per-team AgentRegistry instances.
72
+ * Provides team resolution and isolation boundaries.
73
+ */
74
+ export declare class TeamRegistry {
75
+ private teams;
76
+ constructor(teams: TeamConfig[], deps: WorkerDeps);
77
+ /** Find which team owns an orchestrator ID. */
78
+ resolveTeam(orchestratorId: string): {
79
+ teamId: string;
80
+ registry: AgentRegistry;
81
+ } | null;
82
+ /** Get registry for a specific team by teamId. */
83
+ getTeamRegistry(teamId: string): AgentRegistry | null;
84
+ /** Get raw team config for a specific team by teamId. */
85
+ getTeamConfig(teamId: string): TeamConfig | null;
86
+ /** All teams for UI picker. */
87
+ listTeams(): TeamInfo[];
88
+ /** True if any team has workers. */
89
+ hasWorkers(): boolean;
90
+ }
91
+ /** Parse "provider:modelId" string. */
92
+ export declare function parseModel(raw: string): {
93
+ provider: string;
94
+ modelId: string;
95
+ codexReasoningEffort?: CodexReasoningEffort;
96
+ };
@@ -0,0 +1,141 @@
1
+ import { parseModelSpec } from "../config/model-spec.js";
2
+ import { getModel } from "./providers.js";
3
+ import { resolveModelDef } from "../config/model-catalog.js";
4
+ /**
5
+ * Holds agent configs for a single team.
6
+ * The first config is the orchestrator, rest are workers.
7
+ * `canDelegate` is auto-derived from team membership.
8
+ */
9
+ export class AgentRegistry {
10
+ deps;
11
+ orchestratorConfig;
12
+ /** Workers keyed by name (human-readable, unique within team). */
13
+ workersByName;
14
+ workerNames;
15
+ constructor(orchestrator, workers, deps) {
16
+ this.deps = deps;
17
+ this.orchestratorConfig = orchestrator;
18
+ this.workersByName = new Map(workers.map((w) => [w.name, w]));
19
+ this.workerNames = workers.map((w) => w.name);
20
+ }
21
+ getOrchestrator() {
22
+ return this.orchestratorConfig;
23
+ }
24
+ /** Resolve the orchestrator into a model + def, ready for per-session override. */
25
+ resolveOrchestrator() {
26
+ return this.resolveConfig(this.orchestratorConfig);
27
+ }
28
+ getWorker(name) {
29
+ return this.workersByName.get(name);
30
+ }
31
+ /** Return the list of worker names that the orchestrator can delegate to. */
32
+ delegatableWorkers() {
33
+ return this.workerNames;
34
+ }
35
+ /** Build an id→name map for all agents (orchestrator + workers). */
36
+ buildIdToNameMap() {
37
+ const map = new Map();
38
+ map.set(this.orchestratorConfig.id, this.orchestratorConfig.name);
39
+ for (const w of this.workersByName.values()) {
40
+ map.set(w.id, w.name);
41
+ }
42
+ return map;
43
+ }
44
+ /** Resolve a worker by name into a model + filtered tool set, ready for runLoop(). */
45
+ resolveWorker(name) {
46
+ const cfg = this.workersByName.get(name);
47
+ return cfg ? this.resolveConfig(cfg) : null;
48
+ }
49
+ /** Resolve any team agent by stable ID (orchestrator or worker). */
50
+ resolveAgentById(agentId) {
51
+ if (this.orchestratorConfig.id === agentId) {
52
+ return { role: "orchestrator", resolved: this.resolveOrchestrator() };
53
+ }
54
+ for (const worker of this.workersByName.values()) {
55
+ if (worker.id === agentId) {
56
+ return { role: "worker", resolved: this.resolveConfig(worker) };
57
+ }
58
+ }
59
+ return null;
60
+ }
61
+ resolveConfig(cfg) {
62
+ const { provider, modelId, codexReasoningEffort } = parseModel(cfg.model);
63
+ const model = getModel(provider, modelId, { codexReasoningEffort });
64
+ const modelDef = resolveModelDef(modelId, cfg.contextWindow);
65
+ const tools = filterTools(this.deps.baseTools, cfg.tools);
66
+ return { agentConfig: cfg, model, modelDef, tools };
67
+ }
68
+ }
69
+ /**
70
+ * Wraps per-team AgentRegistry instances.
71
+ * Provides team resolution and isolation boundaries.
72
+ */
73
+ export class TeamRegistry {
74
+ teams;
75
+ constructor(teams, deps) {
76
+ this.teams = new Map(teams.map((t) => [
77
+ t.id,
78
+ {
79
+ config: t,
80
+ registry: new AgentRegistry(t.orchestrator, t.workers, deps),
81
+ },
82
+ ]));
83
+ }
84
+ /** Find which team owns an orchestrator ID. */
85
+ resolveTeam(orchestratorId) {
86
+ for (const [teamId, { config, registry }] of this.teams) {
87
+ if (config.orchestrator.id === orchestratorId) {
88
+ return { teamId, registry };
89
+ }
90
+ }
91
+ return null;
92
+ }
93
+ /** Get registry for a specific team by teamId. */
94
+ getTeamRegistry(teamId) {
95
+ return this.teams.get(teamId)?.registry ?? null;
96
+ }
97
+ /** Get raw team config for a specific team by teamId. */
98
+ getTeamConfig(teamId) {
99
+ return this.teams.get(teamId)?.config ?? null;
100
+ }
101
+ /** All teams for UI picker. */
102
+ listTeams() {
103
+ return [...this.teams.values()].map(({ config }) => ({
104
+ id: config.id,
105
+ name: config.name ?? config.id,
106
+ color: config.color ?? "",
107
+ orchestratorId: config.orchestrator.id,
108
+ orchestratorIdentity: config.orchestrator.identity,
109
+ orchestratorModel: parseModel(config.orchestrator.model).modelId,
110
+ workerCount: config.workers.length,
111
+ }));
112
+ }
113
+ /** True if any team has workers. */
114
+ hasWorkers() {
115
+ for (const { config } of this.teams.values()) {
116
+ if (config.workers.length > 0)
117
+ return true;
118
+ }
119
+ return false;
120
+ }
121
+ }
122
+ /** Parse "provider:modelId" string. */
123
+ export function parseModel(raw) {
124
+ const parsed = parseModelSpec(raw);
125
+ return {
126
+ provider: parsed.provider,
127
+ modelId: parsed.modelId,
128
+ codexReasoningEffort: parsed.codexReasoningEffort,
129
+ };
130
+ }
131
+ /** Return only the tools whose names are in the allowlist, or all if allowlist is empty. */
132
+ function filterTools(base, allowlist) {
133
+ if (allowlist.length === 0)
134
+ return { ...base };
135
+ const filtered = {};
136
+ for (const name of allowlist) {
137
+ if (name in base)
138
+ filtered[name] = base[name];
139
+ }
140
+ return filtered;
141
+ }