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,41 @@
1
+ /**
2
+ * Environment variable filtering for bash tool security.
3
+ *
4
+ * Strips dangerous env vars that could be used for code injection
5
+ * (LD_PRELOAD, DYLD_*, NODE_OPTIONS, etc.) before passing to child processes.
6
+ */
7
+ /** Exact env var names to strip (from Verybot's DANGEROUS_HOST_ENV_VARS). */
8
+ const BLOCKED_VARS = new Set([
9
+ "LD_PRELOAD",
10
+ "LD_LIBRARY_PATH",
11
+ "LD_AUDIT",
12
+ "DYLD_INSERT_LIBRARIES",
13
+ "DYLD_LIBRARY_PATH",
14
+ "NODE_OPTIONS",
15
+ "NODE_PATH",
16
+ "PYTHONPATH",
17
+ "PYTHONHOME",
18
+ "RUBYLIB",
19
+ "PERL5LIB",
20
+ "BASH_ENV",
21
+ "ENV",
22
+ "GCONV_PATH",
23
+ "IFS",
24
+ "SSLKEYLOGFILE",
25
+ ]);
26
+ /** Env var prefixes to strip (catches all DYLD_* and LD_* variants). */
27
+ const BLOCKED_PREFIXES = ["DYLD_", "LD_"];
28
+ /** Returns a copy of process.env with dangerous variables removed. */
29
+ export function sanitizeEnv() {
30
+ const clean = {};
31
+ for (const [key, value] of Object.entries(process.env)) {
32
+ if (value === undefined)
33
+ continue;
34
+ if (BLOCKED_VARS.has(key))
35
+ continue;
36
+ if (BLOCKED_PREFIXES.some((prefix) => key.startsWith(prefix)))
37
+ continue;
38
+ clean[key] = value;
39
+ }
40
+ return clean;
41
+ }
@@ -0,0 +1,33 @@
1
+ import type { Tool } from "ai";
2
+ import type { SkillEntry } from "./types.js";
3
+ /**
4
+ * Manages skills with optional file watching for hot-reload.
5
+ * The Agent reads `systemPrompt` and `readTool` on each run,
6
+ * so changes are picked up automatically.
7
+ */
8
+ export declare class SkillManager {
9
+ private _entries;
10
+ private _systemPrompt;
11
+ private _readTool;
12
+ private watcher;
13
+ private retryTimer;
14
+ private retryCount;
15
+ private stopped;
16
+ private dir;
17
+ constructor(dir: string);
18
+ get entries(): SkillEntry[];
19
+ get systemPrompt(): string;
20
+ get readTool(): Tool | null;
21
+ /** Scan the skills directory and update state. */
22
+ scan(): Promise<void>;
23
+ /** Start watching the skills directory for changes. Retries with backoff if directory is missing. */
24
+ startWatching(): void;
25
+ /** Stop the file watcher and cancel any pending retries. */
26
+ stopWatching(): void;
27
+ private attemptWatch;
28
+ private scheduleRetry;
29
+ }
30
+ /**
31
+ * Load skills from the given directory and start watching for changes.
32
+ */
33
+ export declare function loadSkills(dir: string): Promise<SkillManager>;
@@ -0,0 +1,132 @@
1
+ import { watch as chokidarWatch } from "chokidar";
2
+ import { resolve } from "path";
3
+ import { scanSkills } from "./scanner.js";
4
+ import { buildSkillListing } from "./prompt.js";
5
+ import { createReadSkillTool } from "./read-tool.js";
6
+ import { logger } from "../logger.js";
7
+ /** Debounce delay for file watcher to avoid rapid rescans. */
8
+ const WATCH_DEBOUNCE_MS = 500;
9
+ /** Initial retry delay when watcher fails or directory is missing. */
10
+ const WATCH_RETRY_BASE_MS = 2_000;
11
+ /** Maximum retry delay (caps exponential backoff). */
12
+ const WATCH_RETRY_MAX_MS = 60_000;
13
+ /**
14
+ * Manages skills with optional file watching for hot-reload.
15
+ * The Agent reads `systemPrompt` and `readTool` on each run,
16
+ * so changes are picked up automatically.
17
+ */
18
+ export class SkillManager {
19
+ _entries = [];
20
+ _systemPrompt = "";
21
+ _readTool = null;
22
+ watcher = null;
23
+ retryTimer = null;
24
+ retryCount = 0;
25
+ stopped = false;
26
+ dir;
27
+ constructor(dir) {
28
+ this.dir = resolve(dir);
29
+ }
30
+ get entries() {
31
+ return this._entries;
32
+ }
33
+ get systemPrompt() {
34
+ return this._systemPrompt;
35
+ }
36
+ get readTool() {
37
+ return this._readTool;
38
+ }
39
+ /** Scan the skills directory and update state. */
40
+ async scan() {
41
+ const entries = await scanSkills(this.dir);
42
+ this._entries = entries;
43
+ if (entries.length === 0) {
44
+ this._systemPrompt = "";
45
+ this._readTool = null;
46
+ return;
47
+ }
48
+ this._systemPrompt = buildSkillListing(entries);
49
+ this._readTool = createReadSkillTool(entries);
50
+ logger.info(`Skills loaded: ${entries.map((s) => s.name).join(", ")}`);
51
+ }
52
+ /** Start watching the skills directory for changes. Retries with backoff if directory is missing. */
53
+ startWatching() {
54
+ this.stopped = false;
55
+ this.attemptWatch();
56
+ }
57
+ /** Stop the file watcher and cancel any pending retries. */
58
+ stopWatching() {
59
+ this.stopped = true;
60
+ if (this.retryTimer) {
61
+ clearTimeout(this.retryTimer);
62
+ this.retryTimer = null;
63
+ }
64
+ this.watcher?.close();
65
+ this.watcher = null;
66
+ }
67
+ attemptWatch() {
68
+ if (this.stopped)
69
+ return;
70
+ this.watcher?.close();
71
+ this.watcher = null;
72
+ let debounceTimer = null;
73
+ try {
74
+ this.watcher = chokidarWatch(this.dir, {
75
+ ignoreInitial: true,
76
+ persistent: true,
77
+ depth: 2,
78
+ });
79
+ }
80
+ catch {
81
+ this.scheduleRetry();
82
+ return;
83
+ }
84
+ const debouncedRescan = (path) => {
85
+ if (debounceTimer)
86
+ clearTimeout(debounceTimer);
87
+ debounceTimer = setTimeout(() => {
88
+ logger.info(`Skills directory changed (${path}), rescanning...`);
89
+ this.scan().catch((err) => {
90
+ logger.warn(`Skills rescan failed: ${err instanceof Error ? err.message : err}`);
91
+ });
92
+ }, WATCH_DEBOUNCE_MS);
93
+ };
94
+ this.watcher
95
+ .on("add", debouncedRescan)
96
+ .on("change", debouncedRescan)
97
+ .on("unlink", debouncedRescan)
98
+ .on("unlinkDir", debouncedRescan)
99
+ .on("error", (err) => {
100
+ logger.warn(`Skills watcher error: ${err instanceof Error ? err.message : err}`);
101
+ this.scheduleRetry();
102
+ });
103
+ this.retryCount = 0;
104
+ logger.info(`Watching skills directory: ${this.dir}`);
105
+ }
106
+ scheduleRetry() {
107
+ if (this.stopped)
108
+ return;
109
+ this.watcher?.close();
110
+ this.watcher = null;
111
+ const delay = Math.min(WATCH_RETRY_BASE_MS * 2 ** this.retryCount, WATCH_RETRY_MAX_MS);
112
+ this.retryCount++;
113
+ logger.info(`Skills watcher will retry in ${delay / 1000}s (attempt ${this.retryCount})`);
114
+ this.retryTimer = setTimeout(() => {
115
+ this.retryTimer = null;
116
+ this.scan()
117
+ .catch((err) => {
118
+ logger.warn(`Skills rescan on retry failed: ${err instanceof Error ? err.message : err}`);
119
+ })
120
+ .finally(() => this.attemptWatch());
121
+ }, delay);
122
+ }
123
+ }
124
+ /**
125
+ * Load skills from the given directory and start watching for changes.
126
+ */
127
+ export async function loadSkills(dir) {
128
+ const manager = new SkillManager(dir);
129
+ await manager.scan();
130
+ manager.startWatching();
131
+ return manager;
132
+ }
@@ -0,0 +1,6 @@
1
+ import type { SkillEntry } from "./types.js";
2
+ /**
3
+ * Build a compact system prompt section listing available skills.
4
+ * The model uses `read_skill` to fetch full content on demand.
5
+ */
6
+ export declare function buildSkillListing(skills: SkillEntry[]): string;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Build a compact system prompt section listing available skills.
3
+ * The model uses `read_skill` to fetch full content on demand.
4
+ */
5
+ export function buildSkillListing(skills) {
6
+ if (skills.length === 0)
7
+ return "";
8
+ const lines = skills.map((s) => ` - **${s.name}**: ${s.description || "(no description)"}`);
9
+ return `## Available Skills
10
+ You have access to specialized skills. Use the \`read_skill\` tool to read the full instructions for a skill before applying it.
11
+
12
+ <available_skills>
13
+ ${lines.join("\n")}
14
+ </available_skills>
15
+
16
+ When a user's request matches a skill, read it first, then follow its instructions.`;
17
+ }
@@ -0,0 +1,7 @@
1
+ import { type Tool } from "ai";
2
+ import type { SkillEntry } from "./types.js";
3
+ /**
4
+ * Create the read_skill tool with the given skill entries baked in.
5
+ * Returns the full SKILL.md content for the requested skill name.
6
+ */
7
+ export declare function createReadSkillTool(skills: SkillEntry[]): Tool;
@@ -0,0 +1,24 @@
1
+ import { tool } from "ai";
2
+ import { z } from "zod";
3
+ /**
4
+ * Create the read_skill tool with the given skill entries baked in.
5
+ * Returns the full SKILL.md content for the requested skill name.
6
+ */
7
+ export function createReadSkillTool(skills) {
8
+ const skillMap = new Map(skills.map((s) => [s.name.toLowerCase(), s]));
9
+ const validNames = skills.map((s) => s.name).join(", ");
10
+ return tool({
11
+ description: `Read the full instructions for a skill. Available: ${validNames}. ` +
12
+ `Call this before applying a skill so you know its full instructions.`,
13
+ inputSchema: z.object({
14
+ name: z.string().describe("The skill name to read"),
15
+ }),
16
+ execute: async ({ name }) => {
17
+ const entry = skillMap.get(name.toLowerCase());
18
+ if (!entry) {
19
+ return `Unknown skill "${name}". Available: ${validNames}`;
20
+ }
21
+ return entry.content;
22
+ },
23
+ });
24
+ }
@@ -0,0 +1,6 @@
1
+ import type { SkillEntry } from "./types.js";
2
+ /**
3
+ * Scan `dir` for subdirectories containing SKILL.md files.
4
+ * Returns metadata for each discovered skill.
5
+ */
6
+ export declare function scanSkills(dir: string): Promise<SkillEntry[]>;
@@ -0,0 +1,73 @@
1
+ import { readdir, readFile, stat } from "fs/promises";
2
+ import { join, basename, resolve } from "path";
3
+ import { logger } from "../logger.js";
4
+ const SKILL_FILENAME = "SKILL.md";
5
+ /** Parse optional YAML frontmatter delimited by --- */
6
+ function parseFrontmatter(raw) {
7
+ const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
8
+ if (!match)
9
+ return { meta: {}, body: raw };
10
+ const meta = {};
11
+ for (const line of match[1].split("\n")) {
12
+ const colon = line.indexOf(":");
13
+ if (colon === -1)
14
+ continue;
15
+ const key = line.slice(0, colon).trim();
16
+ let value = line.slice(colon + 1).trim();
17
+ // Handle inline YAML list: tools: [a, b, c]
18
+ if (typeof value === "string" && value.startsWith("[") && value.endsWith("]")) {
19
+ value = value
20
+ .slice(1, -1)
21
+ .split(",")
22
+ .map((s) => s.trim())
23
+ .filter(Boolean);
24
+ }
25
+ meta[key] = value;
26
+ }
27
+ return { meta, body: match[2] };
28
+ }
29
+ /**
30
+ * Scan `dir` for subdirectories containing SKILL.md files.
31
+ * Returns metadata for each discovered skill.
32
+ */
33
+ export async function scanSkills(dir) {
34
+ const absDir = resolve(dir);
35
+ let entries;
36
+ try {
37
+ entries = await readdir(absDir);
38
+ }
39
+ catch {
40
+ logger.info(`Skills directory not found: ${absDir} (skipping)`);
41
+ return [];
42
+ }
43
+ const skills = [];
44
+ const seenNames = new Set();
45
+ for (const entry of entries) {
46
+ const entryPath = join(absDir, entry);
47
+ const entryStat = await stat(entryPath).catch(() => null);
48
+ if (!entryStat?.isDirectory())
49
+ continue;
50
+ const skillFile = join(entryPath, SKILL_FILENAME);
51
+ let raw;
52
+ try {
53
+ raw = await readFile(skillFile, "utf-8");
54
+ }
55
+ catch {
56
+ continue; // no SKILL.md in this directory
57
+ }
58
+ const { meta, body } = parseFrontmatter(raw);
59
+ const name = meta.name ?? basename(entryPath);
60
+ const description = meta.description ?? "";
61
+ const tools = Array.isArray(meta.tools) ? meta.tools : [];
62
+ const icon = meta.icon ?? undefined;
63
+ const normalizedName = name.toLowerCase();
64
+ if (seenNames.has(normalizedName)) {
65
+ logger.warn(`Duplicate skill name "${name}" at ${skillFile}, skipping`);
66
+ continue;
67
+ }
68
+ seenNames.add(normalizedName);
69
+ skills.push({ name, description, content: body, tools, icon, path: skillFile });
70
+ // logger.info(`Loaded skill: ${name}${description ? ` — ${description}` : ""}`);
71
+ }
72
+ return skills;
73
+ }
@@ -0,0 +1,15 @@
1
+ /** Metadata parsed from a SKILL.md frontmatter during startup scan. */
2
+ export interface SkillEntry {
3
+ /** Skill name (from frontmatter `name` or directory name) */
4
+ name: string;
5
+ /** One-line description (from frontmatter `description`) */
6
+ description: string;
7
+ /** Full raw content of SKILL.md (cached for read_skill tool) */
8
+ content: string;
9
+ /** Optional list of tool names this skill references */
10
+ tools: string[];
11
+ /** Optional Lucide icon name */
12
+ icon?: string;
13
+ /** Absolute path to the SKILL.md file */
14
+ path: string;
15
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ export interface InlineAttachmentContentResolution {
2
+ normalizedText: string;
3
+ imageDataUrls: string[];
4
+ }
5
+ /**
6
+ * Resolve `attachment://...` markdown references in text into image data URLs.
7
+ * The returned text replaces inline attachment markdown with `[image attached]`.
8
+ */
9
+ export declare function resolveInlineAttachmentContent(text: string): Promise<InlineAttachmentContentResolution>;
@@ -0,0 +1,64 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { basename, extname, join } from "node:path";
3
+ import { ATTACHMENTS_DIR } from "../paths.js";
4
+ import { EXT_FOR_MIME } from "./types.js";
5
+ const INLINE_TASK_ATTACHMENT_MARKDOWN_PATTERN = /!\[[^\]]*\]\(attachment:\/\/([^)]+)\)/g;
6
+ const INLINE_TASK_ATTACHMENT_PLACEHOLDER = "[image attached]";
7
+ const MIME_FOR_EXT = Object.fromEntries(Object.entries(EXT_FOR_MIME).map(([mime, ext]) => [ext.toLowerCase(), mime]));
8
+ function extractInlineAttachmentIds(text) {
9
+ const ids = [];
10
+ const seenIds = new Set();
11
+ for (const match of text.matchAll(INLINE_TASK_ATTACHMENT_MARKDOWN_PATTERN)) {
12
+ const rawId = (match[1] ?? "").trim();
13
+ if (!rawId || seenIds.has(rawId))
14
+ continue;
15
+ seenIds.add(rawId);
16
+ ids.push(rawId);
17
+ }
18
+ return ids;
19
+ }
20
+ function normalizeAttachmentId(rawId) {
21
+ const candidate = rawId.trim();
22
+ const safe = basename(candidate);
23
+ if (!safe || safe !== candidate || safe.startsWith("."))
24
+ return null;
25
+ return safe;
26
+ }
27
+ function resolveMimeType(fileName) {
28
+ const extension = extname(fileName).toLowerCase();
29
+ return MIME_FOR_EXT[extension] ?? "application/octet-stream";
30
+ }
31
+ async function readAttachmentAsDataUrl(rawId) {
32
+ const safeId = normalizeAttachmentId(rawId);
33
+ if (!safeId)
34
+ return null;
35
+ try {
36
+ const filePath = join(ATTACHMENTS_DIR, safeId);
37
+ const content = await readFile(filePath);
38
+ const mimeType = resolveMimeType(safeId);
39
+ return `data:${mimeType};base64,${content.toString("base64")}`;
40
+ }
41
+ catch {
42
+ return null;
43
+ }
44
+ }
45
+ function replaceInlineAttachmentMarkdown(text) {
46
+ return text
47
+ .replace(INLINE_TASK_ATTACHMENT_MARKDOWN_PATTERN, INLINE_TASK_ATTACHMENT_PLACEHOLDER)
48
+ .replace(/\n{3,}/g, "\n\n")
49
+ .trim();
50
+ }
51
+ /**
52
+ * Resolve `attachment://...` markdown references in text into image data URLs.
53
+ * The returned text replaces inline attachment markdown with `[image attached]`.
54
+ */
55
+ export async function resolveInlineAttachmentContent(text) {
56
+ const attachmentIds = extractInlineAttachmentIds(text);
57
+ if (attachmentIds.length === 0) {
58
+ return { normalizedText: text, imageDataUrls: [] };
59
+ }
60
+ const resolvedImages = await Promise.all(attachmentIds.map((attachmentId) => readAttachmentAsDataUrl(attachmentId)));
61
+ const imageDataUrls = resolvedImages.filter((url) => Boolean(url));
62
+ const normalizedText = replaceInlineAttachmentMarkdown(text);
63
+ return { normalizedText, imageDataUrls };
64
+ }
@@ -0,0 +1,112 @@
1
+ import type { Task, TaskAttachment, TaskComment, TaskStatus, TaskPriority } from "./types.js";
2
+ export interface CreateTaskInput {
3
+ title: string;
4
+ description?: string;
5
+ teamId?: string;
6
+ assignee?: string;
7
+ priority?: TaskPriority;
8
+ status?: TaskStatus;
9
+ needsHumanReview?: boolean;
10
+ attachments?: TaskAttachment[];
11
+ }
12
+ export interface CreateTaskOptions {
13
+ /** Actor that created the task. */
14
+ updatedBy?: string;
15
+ }
16
+ export interface UpdateTaskInput {
17
+ title?: string;
18
+ description?: string | null;
19
+ status?: TaskStatus;
20
+ assignee?: string | null;
21
+ priority?: TaskPriority;
22
+ teamId?: string | null;
23
+ needsHumanReview?: boolean;
24
+ attachments?: TaskAttachment[];
25
+ }
26
+ export interface UpdateTaskOptions {
27
+ /** Whether to clear claim ownership when the task status changes. */
28
+ clearClaimOnStatusChange?: boolean;
29
+ /** Actor that performed the update. */
30
+ updatedBy?: string;
31
+ }
32
+ export interface ListTasksFilter {
33
+ teamId?: string;
34
+ status?: TaskStatus;
35
+ assignee?: string;
36
+ needsHumanReview?: boolean;
37
+ includeArchived?: boolean;
38
+ }
39
+ export interface CreateTaskCommentOptions {
40
+ /** Actor that created the comment. */
41
+ actor?: string;
42
+ }
43
+ export interface UpdateTaskCommentOptions {
44
+ /** Actor that edited the comment. */
45
+ actor?: string;
46
+ }
47
+ export interface DeleteTaskCommentOptions {
48
+ /** Actor that deleted the comment. */
49
+ actor?: string;
50
+ }
51
+ /**
52
+ * SQLite-backed persistence for team tasks.
53
+ * Shares the same DB file as MemoryStore, ScheduleStore, etc.
54
+ */
55
+ export declare class TaskStore {
56
+ private db;
57
+ private constructor();
58
+ static create(dbPath: string): Promise<TaskStore>;
59
+ private createSchema;
60
+ /** Add position column for existing databases that lack it. */
61
+ private migrate;
62
+ /** Initialize or bump task ID counter based on highest numeric task id in DB. */
63
+ private ensureTaskIdCounter;
64
+ /** Allocate next task ID as an incrementing number persisted in SQLite. */
65
+ private nextTaskId;
66
+ /** Get the next position value for a given status (appends to end). */
67
+ private nextPosition;
68
+ /** Get the next position value for a given status (prepends to top). */
69
+ private topPosition;
70
+ create(input: CreateTaskInput, options?: CreateTaskOptions): Task;
71
+ update(id: string, input: UpdateTaskInput, options?: UpdateTaskOptions): Task | null;
72
+ /**
73
+ * Bulk-reorder tasks within a status column. Sets positions 0, 1, 2, ...
74
+ * Reordering is a board-layout concern and must not mutate task freshness fields
75
+ * (for example updated_at), otherwise subscription workers re-run unchanged tasks.
76
+ */
77
+ reorder(status: TaskStatus, orderedIds: string[]): void;
78
+ delete(id: string): boolean;
79
+ getById(id: string): Task | null;
80
+ list(filter?: ListTasksFilter): Task[];
81
+ listComments(taskId: string): TaskComment[];
82
+ addComment(taskId: string, content: string, options?: CreateTaskCommentOptions): TaskComment | null;
83
+ updateComment(id: string, content: string, options?: UpdateTaskCommentOptions): TaskComment | null;
84
+ deleteComment(id: string, options?: DeleteTaskCommentOptions): TaskComment | null;
85
+ /** Bulk-archive all tasks currently in the provided completion status, optionally scoped to a team. */
86
+ archiveDone(teamId?: string, doneStatus?: string, updatedBy?: string): number;
87
+ /**
88
+ * Atomically claim the oldest unclaimed task matching the given status for an agent.
89
+ * Respects assignee: assigned tasks only go to that agent, unassigned to any subscriber.
90
+ */
91
+ claimTask(agentId: string, status: string): Task | null;
92
+ /** Atomically claim a specific task by ID for an agent. Returns the task if claimed, null if already taken. */
93
+ claimTaskById(taskId: string, agentId: string): Task | null;
94
+ /** Release a claimed task so it can be retried. */
95
+ releaseTask(taskId: string): void;
96
+ /**
97
+ * Release claim only when held by the provided agent id.
98
+ * Returns true when a claim was released.
99
+ */
100
+ releaseTaskIfClaimedBy(taskId: string, agentId: string): boolean;
101
+ /**
102
+ * Mark a claimed task as processed by the given agent for the current task version,
103
+ * then release the claim atomically.
104
+ * Returns true when the task was finalized by the expected claim owner.
105
+ */
106
+ finalizeClaimedTaskRun(taskId: string, agentId: string): boolean;
107
+ /** Release claims older than the given cutoff timestamp. Returns count of released claims. */
108
+ cleanupStaleClaims(cutoffTimestamp: number): number;
109
+ close(): void;
110
+ getCommentById(id: string): TaskComment | null;
111
+ private touchTaskAudit;
112
+ }