@agenticmail/enterprise 0.5.311 → 0.5.313

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 (214) hide show
  1. package/README.md +682 -544
  2. package/dist/agent-autonomy-PSXQ4MNP.js +766 -0
  3. package/dist/agent-heartbeat-6H3YAQ32.js +510 -0
  4. package/dist/agent-heartbeat-7WS3XILF.js +510 -0
  5. package/dist/agent-heartbeat-BFGKYUUK.js +510 -0
  6. package/dist/agent-heartbeat-SSV65YTX.js +510 -0
  7. package/dist/agent-heartbeat-T5IIHVF4.js +510 -0
  8. package/dist/agent-heartbeat-X3C6FIU2.js +510 -0
  9. package/dist/agent-tools-BW6CLQQ7.js +13897 -0
  10. package/dist/agent-tools-KEA7QEWF.js +13897 -0
  11. package/dist/agent-tools-NU7V3S5N.js +13899 -0
  12. package/dist/agent-tools-WINDYRQ2.js +13897 -0
  13. package/dist/chunk-3ELH5CU6.js +4910 -0
  14. package/dist/chunk-4QYRS3MS.js +1519 -0
  15. package/dist/chunk-52REEVDW.js +1519 -0
  16. package/dist/chunk-5RZJ76YI.js +4977 -0
  17. package/dist/chunk-6L7FQI5Q.js +4909 -0
  18. package/dist/chunk-763OMGFI.js +1519 -0
  19. package/dist/chunk-7ILSXGY6.js +1519 -0
  20. package/dist/chunk-7UCKD25B.js +551 -0
  21. package/dist/chunk-A6MSR7DL.js +4977 -0
  22. package/dist/chunk-ASD2YB6O.js +1519 -0
  23. package/dist/chunk-AZOIHLLX.js +4977 -0
  24. package/dist/chunk-BDCFOP7O.js +537 -0
  25. package/dist/chunk-BSVWPG6I.js +106 -0
  26. package/dist/chunk-C46DNDZB.js +1519 -0
  27. package/dist/chunk-CFVTK4FQ.js +4977 -0
  28. package/dist/chunk-CHJAOKCJ.js +4921 -0
  29. package/dist/chunk-CYEWTXYH.js +4977 -0
  30. package/dist/chunk-D3KFSWLK.js +48 -0
  31. package/dist/chunk-DUVGNAIY.js +4977 -0
  32. package/dist/chunk-DX4XEFVE.js +25229 -0
  33. package/dist/chunk-EX6FQSEV.js +167 -0
  34. package/dist/chunk-F5VZ5EUH.js +1519 -0
  35. package/dist/chunk-FVUDSPOD.js +4977 -0
  36. package/dist/chunk-G6FTZKJX.js +4977 -0
  37. package/dist/chunk-GFEAZN6Y.js +1519 -0
  38. package/dist/chunk-HKV4FQFW.js +1519 -0
  39. package/dist/chunk-ICCPULDT.js +25217 -0
  40. package/dist/chunk-IYEM627Q.js +25216 -0
  41. package/dist/chunk-JHRJ4QJ6.js +1519 -0
  42. package/dist/chunk-K2DAUYHV.js +4977 -0
  43. package/dist/chunk-KDQDSZZQ.js +4973 -0
  44. package/dist/chunk-LDUD6AZY.js +1519 -0
  45. package/dist/chunk-LES5TJ5L.js +4909 -0
  46. package/dist/chunk-MJGGW6MC.js +106 -0
  47. package/dist/chunk-MQKIWAHQ.js +106 -0
  48. package/dist/chunk-NGA7BBPF.js +48 -0
  49. package/dist/chunk-OE3TI4IQ.js +1519 -0
  50. package/dist/chunk-OHSBIYDR.js +4977 -0
  51. package/dist/chunk-OPOBUYJT.js +1519 -0
  52. package/dist/chunk-OZEYDEPB.js +1519 -0
  53. package/dist/chunk-P4PODSQH.js +1519 -0
  54. package/dist/chunk-P7UOSFIE.js +636 -0
  55. package/dist/chunk-PFN6DODU.js +4921 -0
  56. package/dist/chunk-PKDVM4IY.js +4917 -0
  57. package/dist/chunk-Q5KG3G7U.js +25115 -0
  58. package/dist/chunk-QMVNW4FJ.js +25229 -0
  59. package/dist/chunk-QZ5UPRBE.js +4977 -0
  60. package/dist/chunk-SPP23N42.js +4977 -0
  61. package/dist/chunk-SRGHNFOY.js +4921 -0
  62. package/dist/chunk-TPLVQFXM.js +2594 -0
  63. package/dist/chunk-U3XYF4QP.js +4977 -0
  64. package/dist/chunk-VRRJH2DY.js +4921 -0
  65. package/dist/chunk-WY42BS3F.js +1519 -0
  66. package/dist/chunk-XAA4VHHZ.js +1519 -0
  67. package/dist/chunk-Y2KIY4BA.js +4969 -0
  68. package/dist/chunk-Z5Y5KTPC.js +4977 -0
  69. package/dist/chunk-ZA4QRACH.js +4977 -0
  70. package/dist/chunk-ZHLGSTXF.js +4909 -0
  71. package/dist/cli-agent-26BUULHZ.js +2169 -0
  72. package/dist/cli-agent-2FLJWXOC.js +2169 -0
  73. package/dist/cli-agent-4NNQFLO6.js +2255 -0
  74. package/dist/cli-agent-5WV3EEPW.js +2252 -0
  75. package/dist/cli-agent-65JUT6DU.js +2193 -0
  76. package/dist/cli-agent-6HLL7A5K.js +2255 -0
  77. package/dist/cli-agent-CZ26QWUZ.js +2210 -0
  78. package/dist/cli-agent-HPVSWDNQ.js +2255 -0
  79. package/dist/cli-agent-K4SBVG5X.js +2210 -0
  80. package/dist/cli-agent-K5D424X2.js +2252 -0
  81. package/dist/cli-agent-U4OL5FGK.js +2210 -0
  82. package/dist/cli-agent-WUMPOIKQ.js +2169 -0
  83. package/dist/cli-agent-WWRGGJ2F.js +2255 -0
  84. package/dist/cli-agent-ZDBBTVGU.js +2193 -0
  85. package/dist/cli-agent-ZIZ5JP4O.js +2252 -0
  86. package/dist/cli-recover-I4KNR2OI.js +487 -0
  87. package/dist/cli-recover-IQTUKWR2.js +487 -0
  88. package/dist/cli-recover-OYJHELOR.js +487 -0
  89. package/dist/cli-recover-PVQC7UXB.js +487 -0
  90. package/dist/cli-recover-T32NABFA.js +487 -0
  91. package/dist/cli-serve-FE4CMMSN.js +143 -0
  92. package/dist/cli-serve-FTQJ3RUK.js +143 -0
  93. package/dist/cli-serve-G4PUCASH.js +143 -0
  94. package/dist/cli-serve-HBZYUUQ3.js +143 -0
  95. package/dist/cli-serve-L3NUROMO.js +143 -0
  96. package/dist/cli-serve-LAA5WIZK.js +143 -0
  97. package/dist/cli-serve-LV4TUSJD.js +143 -0
  98. package/dist/cli-serve-MFCTVA2L.js +140 -0
  99. package/dist/cli-serve-QCRUFI5B.js +143 -0
  100. package/dist/cli-serve-S7OGQN4P.js +143 -0
  101. package/dist/cli-serve-SI4BQRXT.js +140 -0
  102. package/dist/cli-serve-UNB7EHN4.js +143 -0
  103. package/dist/cli-serve-UV3GVDRD.js +143 -0
  104. package/dist/cli-serve-V5QICXR5.js +143 -0
  105. package/dist/cli-serve-VG6Z6GIB.js +143 -0
  106. package/dist/cli-serve-XSYHPGZI.js +143 -0
  107. package/dist/cli-serve-Y534FCRV.js +140 -0
  108. package/dist/cli-verify-CZIITRED.js +149 -0
  109. package/dist/cli-verify-N73GOKEF.js +149 -0
  110. package/dist/cli-verify-QEEBZOUZ.js +149 -0
  111. package/dist/cli-verify-RC5HI6DU.js +149 -0
  112. package/dist/cli-verify-VKBNIEAX.js +149 -0
  113. package/dist/cli.js +5 -5
  114. package/dist/dashboard/app.js +16 -3
  115. package/dist/dashboard/components/org-switcher.js +5 -1
  116. package/dist/dashboard/org-switcher.js +156 -0
  117. package/dist/dashboard/pages/login.js +160 -4
  118. package/dist/dashboard/pages/task-pipeline.js +1 -1
  119. package/dist/factory-3IWXVE37.js +9 -0
  120. package/dist/factory-5M6PTMLC.js +11 -0
  121. package/dist/factory-CSSHN7GE.js +11 -0
  122. package/dist/factory-JFWXTAWK.js +11 -0
  123. package/dist/factory-TBGUYM5X.js +9 -0
  124. package/dist/google-W5AYGNUJ.js +33 -0
  125. package/dist/index.js +6 -6
  126. package/dist/meetings-FJ453ENF.js +12 -0
  127. package/dist/postgres-BCHZWRU3.js +832 -0
  128. package/dist/postgres-BI4QVRM6.js +825 -0
  129. package/dist/postgres-BOTHOPDW.js +875 -0
  130. package/dist/postgres-JBUKR3TA.js +873 -0
  131. package/dist/postgres-Z7QYSU6K.js +861 -0
  132. package/dist/routes-7QYAQTWA.js +90 -0
  133. package/dist/routes-JCBVZU54.js +90 -0
  134. package/dist/routes-KEDEJFRE.js +90 -0
  135. package/dist/routes-WI64ADVH.js +90 -0
  136. package/dist/routes-X36OSCID.js +90 -0
  137. package/dist/runtime-75XR6KEW.js +45 -0
  138. package/dist/runtime-BNM7ZNNL.js +45 -0
  139. package/dist/runtime-ES6WCJ7D.js +45 -0
  140. package/dist/runtime-KYJTML2B.js +45 -0
  141. package/dist/runtime-LO67ZHQA.js +45 -0
  142. package/dist/runtime-VIXKKVSZ.js +45 -0
  143. package/dist/runtime-WHWJPCGK.js +45 -0
  144. package/dist/runtime-Z2Q6GUHH.js +45 -0
  145. package/dist/runtime-ZZ6CALSB.js +45 -0
  146. package/dist/server-27A4WEJC.js +28 -0
  147. package/dist/server-2CBXP4WS.js +28 -0
  148. package/dist/server-4JQAB5R4.js +28 -0
  149. package/dist/server-6BOM5U64.js +28 -0
  150. package/dist/server-CA2I3LJY.js +28 -0
  151. package/dist/server-FLJKNPRD.js +28 -0
  152. package/dist/server-HMIHIQ2N.js +28 -0
  153. package/dist/server-KIXXLR2D.js +28 -0
  154. package/dist/server-KSEIZTXF.js +28 -0
  155. package/dist/server-MPVW7DKZ.js +28 -0
  156. package/dist/server-PRTVRQ2D.js +28 -0
  157. package/dist/server-SYIG6HAX.js +28 -0
  158. package/dist/server-U32KDIXC.js +28 -0
  159. package/dist/server-WFN6CA4T.js +28 -0
  160. package/dist/server-XQUE3FGT.js +28 -0
  161. package/dist/server-XWT2UORK.js +28 -0
  162. package/dist/server-Y3BGNN5Q.js +28 -0
  163. package/dist/setup-352L2TPS.js +20 -0
  164. package/dist/setup-4MM645XK.js +20 -0
  165. package/dist/setup-5JPWW6IP.js +20 -0
  166. package/dist/setup-CUN6LVUV.js +20 -0
  167. package/dist/setup-D3YHPWPY.js +20 -0
  168. package/dist/setup-D4A5I6UM.js +20 -0
  169. package/dist/setup-DOPLXTB3.js +20 -0
  170. package/dist/setup-E3NSIM6B.js +20 -0
  171. package/dist/setup-E3V2D7NL.js +20 -0
  172. package/dist/setup-FSYPGI2C.js +20 -0
  173. package/dist/setup-G3RPKRG3.js +20 -0
  174. package/dist/setup-KJ77HNWK.js +20 -0
  175. package/dist/setup-LPSOY5V5.js +20 -0
  176. package/dist/setup-N3ODDSQE.js +20 -0
  177. package/dist/setup-NLDM3M2P.js +20 -0
  178. package/dist/setup-PT6WGOYB.js +20 -0
  179. package/dist/setup-SWJMNDWF.js +20 -0
  180. package/dist/system-prompts-6OUTAMH6.js +41 -0
  181. package/dist/task-queue-YP2I54IA.js +9 -0
  182. package/dist/telegram-QRNGRT5M.js +17 -0
  183. package/dist/whatsapp-VYVINCGV.js +31 -0
  184. package/god_is_great.html +35 -0
  185. package/package.json +1 -1
  186. package/src/admin/routes.ts +24 -4
  187. package/src/agent-tools/index.ts +4 -1
  188. package/src/agent-tools/tool-resolver.ts +15 -4
  189. package/src/agent-tools/tools/browser.ts +2 -2
  190. package/src/agent-tools/tools/local/dependency-manager.ts +286 -0
  191. package/src/agent-tools/tools/local/index.ts +3 -0
  192. package/src/agent-tools/tools/messaging/telegram.ts +29 -0
  193. package/src/agent-tools/tools/messaging/whatsapp.ts +59 -4
  194. package/src/auth/routes.ts +15 -12
  195. package/src/cli-agent.ts +47 -6
  196. package/src/cli-serve.ts +2 -5
  197. package/src/dashboard/app.js +16 -3
  198. package/src/dashboard/components/org-switcher.js +5 -1
  199. package/src/dashboard/pages/login.js +160 -4
  200. package/src/dashboard/pages/task-pipeline.js +1 -1
  201. package/src/db/adapter.ts +2 -0
  202. package/src/db/factory.ts +78 -0
  203. package/src/db/postgres.ts +57 -12
  204. package/src/engine/agent-autonomy.ts +1 -1
  205. package/src/engine/agent-heartbeat.ts +1 -1
  206. package/src/engine/messaging-poller.ts +146 -11
  207. package/src/engine/oauth-connect-routes.ts +23 -3
  208. package/src/engine/routes.ts +1 -1
  209. package/src/engine/task-poller.ts +54 -3
  210. package/src/engine/task-queue.ts +30 -0
  211. package/src/runtime/index.ts +2 -1
  212. package/src/runtime/types.ts +2 -0
  213. package/src/server.ts +43 -1
  214. package/src/system-prompts/triage.ts +1 -1
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Dependency Manager — Detects missing tools/packages and handles installation.
3
+ *
4
+ * Agents use this to:
5
+ * 1. Check if a command/tool is available before using it
6
+ * 2. Request installation if missing (with manager approval flow)
7
+ * 3. Track what was installed for cleanup
8
+ *
9
+ * Installation approval modes:
10
+ * - 'auto': Install without asking (for trusted/common tools)
11
+ * - 'ask_manager': Message manager for approval before installing
12
+ * - 'deny': Never install (locked-down environments)
13
+ */
14
+
15
+ import { exec as cpExec } from 'node:child_process';
16
+ import { promisify } from 'node:util';
17
+ import { platform } from 'node:os';
18
+ import type { ToolDefinition } from '../../types.js';
19
+
20
+ var execAsync = promisify(cpExec);
21
+
22
+ // Track installations per session for cleanup
23
+ var sessionInstalls = new Map<string, string[]>();
24
+
25
+ // Common tools and their package names per platform
26
+ var KNOWN_PACKAGES: Record<string, { brew?: string; apt?: string; pip?: string; npm?: string; description: string }> = {
27
+ ffmpeg: { brew: 'ffmpeg', apt: 'ffmpeg', description: 'Audio/video processing toolkit' },
28
+ ffprobe: { brew: 'ffmpeg', apt: 'ffmpeg', description: 'Audio/video analysis (part of ffmpeg)' },
29
+ convert: { brew: 'imagemagick', apt: 'imagemagick', description: 'Image manipulation (ImageMagick)' },
30
+ magick: { brew: 'imagemagick', apt: 'imagemagick', description: 'Image manipulation (ImageMagick)' },
31
+ sox: { brew: 'sox', apt: 'sox', description: 'Sound processing toolkit' },
32
+ jq: { brew: 'jq', apt: 'jq', description: 'JSON processor' },
33
+ curl: { brew: 'curl', apt: 'curl', description: 'HTTP client' },
34
+ wget: { brew: 'wget', apt: 'wget', description: 'File downloader' },
35
+ yt_dlp: { brew: 'yt-dlp', pip: 'yt-dlp', description: 'Video downloader' },
36
+ 'yt-dlp': { brew: 'yt-dlp', pip: 'yt-dlp', description: 'Video downloader' },
37
+ tesseract: { brew: 'tesseract', apt: 'tesseract-ocr', description: 'OCR text recognition' },
38
+ pdftk: { brew: 'pdftk-java', apt: 'pdftk', description: 'PDF toolkit' },
39
+ ghostscript: { brew: 'ghostscript', apt: 'ghostscript', description: 'PDF/PostScript processor' },
40
+ gs: { brew: 'ghostscript', apt: 'ghostscript', description: 'PDF/PostScript processor' },
41
+ chromium: { brew: 'chromium', apt: 'chromium-browser', description: 'Web browser' },
42
+ pandoc: { brew: 'pandoc', apt: 'pandoc', description: 'Document format converter' },
43
+ rsvg: { brew: 'librsvg', apt: 'librsvg2-bin', description: 'SVG renderer' },
44
+ graphviz: { brew: 'graphviz', apt: 'graphviz', description: 'Graph visualization' },
45
+ dot: { brew: 'graphviz', apt: 'graphviz', description: 'Graph visualization (part of graphviz)' },
46
+ gifsicle: { brew: 'gifsicle', apt: 'gifsicle', description: 'GIF optimizer' },
47
+ optipng: { brew: 'optipng', apt: 'optipng', description: 'PNG optimizer' },
48
+ qrencode: { brew: 'qrencode', apt: 'qrencode', description: 'QR code generator' },
49
+ htop: { brew: 'htop', apt: 'htop', description: 'Process monitor' },
50
+ tree: { brew: 'tree', apt: 'tree', description: 'Directory tree viewer' },
51
+ ripgrep: { brew: 'ripgrep', apt: 'ripgrep', description: 'Fast text search (rg)' },
52
+ rg: { brew: 'ripgrep', apt: 'ripgrep', description: 'Fast text search' },
53
+ fd: { brew: 'fd', apt: 'fd-find', description: 'Fast file finder' },
54
+ bat: { brew: 'bat', apt: 'bat', description: 'Better cat with syntax highlighting' },
55
+ };
56
+
57
+ export function createDependencyManagerTools(): ToolDefinition[] {
58
+ return [
59
+ {
60
+ name: 'check_dependency',
61
+ description: `Check if a command-line tool is available on this system. Returns availability, version info, and install instructions if missing. Use this BEFORE running shell commands that depend on specific tools (ffmpeg, imagemagick, etc.). If the tool is missing, use install_dependency to install it.`,
62
+ input_schema: {
63
+ type: 'object' as const,
64
+ properties: {
65
+ command: { type: 'string', description: 'Command to check (e.g. "ffmpeg", "convert", "jq")' },
66
+ },
67
+ required: ['command'],
68
+ },
69
+ execute: async (input: any) => {
70
+ var cmd = input.command.trim();
71
+ try {
72
+ var { stdout } = await execAsync(`which ${cmd} 2>/dev/null && ${cmd} --version 2>&1 || ${cmd} -version 2>&1 || echo "version-unknown"`, {
73
+ timeout: 5000,
74
+ env: { ...process.env, TERM: 'dumb' },
75
+ });
76
+ return {
77
+ available: true,
78
+ command: cmd,
79
+ path: stdout.split('\n')[0] || '',
80
+ versionInfo: stdout.slice(0, 500),
81
+ };
82
+ } catch {
83
+ var known = KNOWN_PACKAGES[cmd];
84
+ var os = platform();
85
+ var installCmd = '';
86
+ if (known) {
87
+ if (os === 'darwin' && known.brew) installCmd = `brew install ${known.brew}`;
88
+ else if (known.apt) installCmd = `sudo apt-get install -y ${known.apt}`;
89
+ else if (known.pip) installCmd = `pip install ${known.pip}`;
90
+ else if (known.npm) installCmd = `npm install -g ${known.npm}`;
91
+ }
92
+ return {
93
+ available: false,
94
+ command: cmd,
95
+ description: known?.description || 'Unknown tool',
96
+ installCommand: installCmd || `Use shell_install with package="${cmd}"`,
97
+ suggestion: `Tool "${cmd}" is not installed. ${installCmd ? `Install with: ${installCmd}` : 'Use install_dependency to install it.'}`,
98
+ };
99
+ }
100
+ },
101
+ },
102
+ {
103
+ name: 'install_dependency',
104
+ description: `Install a missing command-line tool using the system package manager (brew on macOS, apt on Linux). Auto-detects the correct package name for common tools. Tracks installations for cleanup. Use check_dependency first to verify the tool is actually missing.`,
105
+ input_schema: {
106
+ type: 'object' as const,
107
+ properties: {
108
+ command: { type: 'string', description: 'Tool name to install (e.g. "ffmpeg", "imagemagick")' },
109
+ package: { type: 'string', description: 'Override package name if different from command' },
110
+ method: { type: 'string', description: 'Force install method: brew|apt|pip|npm (auto-detected by default)' },
111
+ },
112
+ required: ['command'],
113
+ },
114
+ execute: async (input: any) => {
115
+ var cmd = input.command.trim();
116
+ var os = platform();
117
+ var known = KNOWN_PACKAGES[cmd];
118
+ var pkg = input.package || '';
119
+ var method = input.method || '';
120
+
121
+ // Determine install command
122
+ var installCmd = '';
123
+ var packageName = '';
124
+
125
+ if (method === 'pip' || (!method && known?.pip && !known?.brew && !known?.apt)) {
126
+ packageName = pkg || known?.pip || cmd;
127
+ installCmd = `pip install ${packageName}`;
128
+ } else if (method === 'npm' || (!method && known?.npm)) {
129
+ packageName = pkg || known?.npm || cmd;
130
+ installCmd = `npm install -g ${packageName}`;
131
+ } else if (os === 'darwin') {
132
+ packageName = pkg || known?.brew || cmd;
133
+ installCmd = `brew install ${packageName}`;
134
+ } else {
135
+ // Linux
136
+ packageName = pkg || known?.apt || cmd;
137
+ installCmd = `sudo apt-get install -y ${packageName}`;
138
+ }
139
+
140
+ console.log(`[dep-manager] Installing: ${installCmd}`);
141
+
142
+ try {
143
+ var r = await execAsync(installCmd, {
144
+ timeout: 300000, // 5 min for large packages
145
+ maxBuffer: 2 * 1024 * 1024,
146
+ env: { ...process.env, DEBIAN_FRONTEND: 'noninteractive', TERM: 'dumb', HOMEBREW_NO_AUTO_UPDATE: '1' },
147
+ });
148
+
149
+ // Track installation for cleanup
150
+ var key = 'global'; // Could be per-session if we had session context
151
+ var installed = sessionInstalls.get(key) || [];
152
+ installed.push(packageName);
153
+ sessionInstalls.set(key, installed);
154
+
155
+ // Verify installation
156
+ try {
157
+ await execAsync(`which ${cmd}`, { timeout: 3000 });
158
+ return {
159
+ ok: true,
160
+ command: cmd,
161
+ package: packageName,
162
+ method: installCmd.split(' ')[0],
163
+ stdout: (r.stdout || '').slice(-2000),
164
+ note: `Installed "${packageName}" successfully. The "${cmd}" command is now available.`,
165
+ };
166
+ } catch {
167
+ return {
168
+ ok: true,
169
+ command: cmd,
170
+ package: packageName,
171
+ warning: `Package installed but "${cmd}" not found in PATH. The binary may have a different name.`,
172
+ stdout: (r.stdout || '').slice(-2000),
173
+ };
174
+ }
175
+ } catch (err: any) {
176
+ return {
177
+ ok: false,
178
+ command: cmd,
179
+ error: (err.stderr || err.message || '').slice(0, 5000),
180
+ suggestion: `Installation failed. Try: 1) Check package name is correct, 2) Run with sudo if needed, 3) Use shell_exec to install manually.`,
181
+ };
182
+ }
183
+ },
184
+ },
185
+ {
186
+ name: 'check_environment',
187
+ description: 'Check the current system environment: OS, package manager, installed common tools, available disk space, etc. Use at the start of complex tasks to understand capabilities.',
188
+ input_schema: {
189
+ type: 'object' as const,
190
+ properties: {},
191
+ required: [],
192
+ },
193
+ execute: async () => {
194
+ var os = platform();
195
+ var checks: Record<string, boolean> = {};
196
+ var commonTools = ['ffmpeg', 'convert', 'jq', 'curl', 'wget', 'sox', 'pandoc', 'tesseract', 'yt-dlp', 'rg', 'node', 'python3', 'pip3', 'git'];
197
+
198
+ for (var tool of commonTools) {
199
+ try {
200
+ await execAsync(`which ${tool}`, { timeout: 2000 });
201
+ checks[tool] = true;
202
+ } catch {
203
+ checks[tool] = false;
204
+ }
205
+ }
206
+
207
+ var pkgManager = 'unknown';
208
+ try {
209
+ if (os === 'darwin') { await execAsync('which brew', { timeout: 2000 }); pkgManager = 'brew'; }
210
+ else { await execAsync('which apt-get', { timeout: 2000 }); pkgManager = 'apt'; }
211
+ } catch {
212
+ try { await execAsync('which dnf', { timeout: 2000 }); pkgManager = 'dnf'; }
213
+ catch { try { await execAsync('which pacman', { timeout: 2000 }); pkgManager = 'pacman'; } catch {} }
214
+ }
215
+
216
+ var diskSpace = '';
217
+ try {
218
+ var r = await execAsync('df -h / | tail -1', { timeout: 3000 });
219
+ diskSpace = r.stdout.trim();
220
+ } catch {}
221
+
222
+ var nodeVersion = '';
223
+ try { nodeVersion = (await execAsync('node --version', { timeout: 2000 })).stdout.trim(); } catch {}
224
+
225
+ return {
226
+ os,
227
+ arch: process.arch,
228
+ packageManager: pkgManager,
229
+ nodeVersion,
230
+ diskSpace,
231
+ tools: checks,
232
+ missingTools: Object.entries(checks).filter(([, v]) => !v).map(([k]) => k),
233
+ availableTools: Object.entries(checks).filter(([, v]) => v).map(([k]) => k),
234
+ installedThisSession: sessionInstalls.get('global') || [],
235
+ };
236
+ },
237
+ },
238
+ {
239
+ name: 'cleanup_installed',
240
+ description: 'List or uninstall packages that were installed during this session. Use for cleanup after completing tasks.',
241
+ input_schema: {
242
+ type: 'object' as const,
243
+ properties: {
244
+ action: { type: 'string', description: '"list" to see installed packages, "uninstall" to remove them, "keep" to clear tracking without removing' },
245
+ packages: { type: 'array', items: { type: 'string' }, description: 'Specific packages to uninstall (default: all session-installed)' },
246
+ },
247
+ required: ['action'],
248
+ },
249
+ execute: async (input: any) => {
250
+ var installed = sessionInstalls.get('global') || [];
251
+
252
+ if (input.action === 'list') {
253
+ return { installedThisSession: installed };
254
+ }
255
+
256
+ if (input.action === 'keep') {
257
+ sessionInstalls.delete('global');
258
+ return { ok: true, message: 'Tracking cleared. Packages remain installed.', packages: installed };
259
+ }
260
+
261
+ if (input.action === 'uninstall') {
262
+ var toRemove = input.packages || installed;
263
+ var os = platform();
264
+ var results: any[] = [];
265
+
266
+ for (var pkg of toRemove) {
267
+ try {
268
+ var cmd = os === 'darwin' ? `brew uninstall ${pkg}` : `sudo apt-get remove -y ${pkg}`;
269
+ await execAsync(cmd, { timeout: 60000, env: { ...process.env, DEBIAN_FRONTEND: 'noninteractive' } });
270
+ results.push({ package: pkg, removed: true });
271
+ // Remove from tracking
272
+ installed = installed.filter((p: string) => p !== pkg);
273
+ } catch (err: any) {
274
+ results.push({ package: pkg, removed: false, error: (err.message || '').slice(0, 200) });
275
+ }
276
+ }
277
+
278
+ sessionInstalls.set('global', installed);
279
+ return { results, remaining: installed };
280
+ }
281
+
282
+ return { error: 'Unknown action. Use "list", "uninstall", or "keep".' };
283
+ },
284
+ },
285
+ ];
286
+ }
@@ -11,6 +11,7 @@ import { createFileListTool } from './file-list.js';
11
11
  import { createFileSearchTool } from './file-search.js';
12
12
  import { createFileMoveTool, createFileDeleteTool } from './file-ops.js';
13
13
  import { createShellTools } from './shell.js';
14
+ import { createDependencyManagerTools } from './dependency-manager.js';
14
15
  import { createSystemInfoTool } from './system-info.js';
15
16
  import { createCodingTools } from './coding.js';
16
17
  import type { ToolDefinition } from '../../types.js';
@@ -36,6 +37,7 @@ export function createLocalSystemTools(config?: LocalToolsConfig): ToolDefinitio
36
37
  createFileMoveTool(sandbox),
37
38
  createFileDeleteTool(sandbox),
38
39
  ...createShellTools({ cwd: config?.shellCwd, timeout: config?.shellTimeout }),
40
+ ...createDependencyManagerTools(),
39
41
  createSystemInfoTool(),
40
42
  ...createCodingTools({ cwd: config?.shellCwd, sandbox }),
41
43
  ];
@@ -50,3 +52,4 @@ export { createFileMoveTool, createFileDeleteTool } from './file-ops.js';
50
52
  export { createShellTools, createShellExecTool } from './shell.js';
51
53
  export { createSystemInfoTool } from './system-info.js';
52
54
  export { createCodingTools } from './coding.js';
55
+ export { createDependencyManagerTools } from './dependency-manager.js';
@@ -90,6 +90,35 @@ export function createTelegramTools(config: TelegramConfig): ToolDefinition[] {
90
90
  input_schema: { type: 'object' as const, properties: {}, required: [] },
91
91
  execute: async (_id: string) => tgApi(botToken, 'getMe'),
92
92
  },
93
+ {
94
+ name: 'telegram_download_file',
95
+ description: 'Download a file from Telegram by file_id (from media messages). Returns the local path.',
96
+ input_schema: {
97
+ type: 'object' as const,
98
+ properties: {
99
+ fileId: { type: 'string', description: 'Telegram file_id from the message' },
100
+ fileName: { type: 'string', description: 'Optional output filename' },
101
+ },
102
+ required: ['fileId'],
103
+ },
104
+ execute: async (_id: string, input: any) => {
105
+ var fileData = await tgApi(botToken, 'getFile', { file_id: input.fileId });
106
+ if (!fileData.file_path) return { error: 'No file_path returned' };
107
+ var downloadUrl = `https://api.telegram.org/file/bot${botToken}/${fileData.file_path}`;
108
+ var resp = await fetch(downloadUrl);
109
+ if (!resp.ok) throw new Error(`Download failed: ${resp.status}`);
110
+ var { join } = await import('path');
111
+ var { mkdirSync, writeFileSync } = await import('fs');
112
+ var mediaDir = join('/tmp/agents/media');
113
+ try { mkdirSync(mediaDir, { recursive: true }); } catch {}
114
+ var ext = fileData.file_path.split('.').pop() || 'bin';
115
+ var localName = input.fileName || `telegram-${Date.now()}.${ext}`;
116
+ var localPath = join(mediaDir, localName);
117
+ var buffer = Buffer.from(await resp.arrayBuffer());
118
+ writeFileSync(localPath, buffer);
119
+ return { ok: true, localPath, size: buffer.length, originalPath: fileData.file_path };
120
+ },
121
+ },
93
122
  {
94
123
  name: 'telegram_get_chat',
95
124
  description: 'Get chat/group details.',
@@ -155,22 +155,77 @@ export async function getOrCreateConnection(config: WhatsAppConfig): Promise<Wha
155
155
  if (!isSelfChat) continue;
156
156
  }
157
157
 
158
- if (!text) continue;
158
+ // Detect media
159
+ var mediaMessage = msg.message?.imageMessage || msg.message?.videoMessage ||
160
+ msg.message?.documentMessage || msg.message?.audioMessage ||
161
+ msg.message?.stickerMessage || null;
162
+ var hasMedia = !!mediaMessage;
163
+
164
+ // For media messages, caption may be the text
165
+ if (!text && mediaMessage) {
166
+ text = mediaMessage.caption || '';
167
+ }
168
+
169
+ // Skip if no text AND no media
170
+ if (!text && !hasMedia) continue;
159
171
 
160
172
  var senderJid = isGroup ? msg.key.participant : remoteJid;
161
- console.log(`[wa:${config.agentId.slice(0,8)}] Incoming from=${senderJid} group=${isGroup} selfChat=${!!isSelfChat} text="${text.slice(0,50)}"`);
173
+ console.log(`[wa:${config.agentId.slice(0,8)}] Incoming from=${senderJid} group=${isGroup} selfChat=${!!isSelfChat} hasMedia=${hasMedia} text="${(text||'').slice(0,50)}"`);
162
174
 
163
175
  // Show typing indicator while agent processes
164
176
  try { await sock.sendPresenceUpdate('composing', remoteJid); } catch {}
165
177
 
178
+ // Download media if present
179
+ var mediaPath: string | undefined;
180
+ var mediaType: string | undefined;
181
+ if (hasMedia && mediaMessage) {
182
+ try {
183
+ var { downloadMediaMessage } = await import('@whiskeysockets/baileys');
184
+ var buffer = await downloadMediaMessage(msg, 'buffer', {});
185
+ if (buffer && buffer.length > 0) {
186
+ var { join } = await import('path');
187
+ var { mkdirSync, writeFileSync } = await import('fs');
188
+ var mediaDir = join(config.dataDir || `/tmp/agents/${config.agentId}`, 'media');
189
+ try { mkdirSync(mediaDir, { recursive: true }); } catch {}
190
+
191
+ mediaType = msg.message?.imageMessage ? 'photo'
192
+ : msg.message?.videoMessage ? 'video'
193
+ : msg.message?.audioMessage ? 'audio'
194
+ : msg.message?.documentMessage ? 'document'
195
+ : msg.message?.stickerMessage ? 'sticker' : 'file';
196
+
197
+ var ext = mediaType === 'photo' ? 'jpg' : mediaType === 'video' ? 'mp4'
198
+ : mediaType === 'audio' ? 'ogg' : mediaType === 'sticker' ? 'webp'
199
+ : (mediaMessage.fileName?.split('.').pop() || 'bin');
200
+ var localName = mediaMessage.fileName || `${mediaType}-${Date.now()}.${ext}`;
201
+ mediaPath = join(mediaDir, localName);
202
+ writeFileSync(mediaPath, buffer);
203
+ console.log(`[wa:${config.agentId.slice(0,8)}] Media downloaded: ${mediaPath} (${buffer.length} bytes)`);
204
+ }
205
+ } catch (dlErr: any) {
206
+ console.error(`[wa:${config.agentId.slice(0,8)}] Media download failed: ${dlErr.message}`);
207
+ }
208
+ }
209
+
210
+ // Build text with media info
211
+ var finalText = text || '';
212
+ if (mediaPath) {
213
+ var desc = `[${mediaType}${mediaMessage?.fileName ? ': ' + mediaMessage.fileName : ''}] saved to: ${mediaPath}`;
214
+ finalText = text ? `${text}\n\n${desc}` : desc;
215
+ } else if (hasMedia && !text) {
216
+ finalText = `[${mediaType || 'media'} received but download failed]`;
217
+ }
218
+
166
219
  conn.lastMessageAt = Date.now();
167
220
  conn.events.emit('message', {
168
221
  from: remoteJid,
169
222
  senderJid,
170
223
  pushName: msg.pushName,
171
- text,
224
+ text: finalText,
172
225
  timestamp: msg.messageTimestamp,
173
- hasMedia: !!(msg.message?.imageMessage || msg.message?.videoMessage || msg.message?.documentMessage || msg.message?.audioMessage),
226
+ hasMedia,
227
+ mediaPath,
228
+ mediaType,
174
229
  messageId: msg.key.id,
175
230
  isGroup,
176
231
  isSelfChat: !!isSelfChat,
@@ -73,11 +73,14 @@ export function createAuthRoutes(
73
73
  return process.env.NODE_ENV === 'production' || process.env.SECURE_COOKIES === '1';
74
74
  };
75
75
 
76
- async function issueTokens(userId: string, email: string, role: string) {
76
+ async function issueTokens(userId: string, email: string, role: string, clientOrgId?: string | null) {
77
77
  const { SignJWT } = await import('jose');
78
78
  const secret = new TextEncoder().encode(jwtSecret);
79
79
 
80
- const token = await new SignJWT({ sub: userId, email, role })
80
+ const payload: Record<string, any> = { sub: userId, email, role };
81
+ if (clientOrgId) payload.clientOrgId = clientOrgId;
82
+
83
+ const token = await new SignJWT(payload)
81
84
  .setProtectedHeader({ alg: 'HS256' })
82
85
  .setIssuedAt()
83
86
  .setExpirationTime(TOKEN_TTL)
@@ -93,8 +96,8 @@ export function createAuthRoutes(
93
96
  }
94
97
 
95
98
  /** Set session cookies and return token info */
96
- async function setSessionCookies(c: any, userId: string, email: string, role: string, method: string) {
97
- const { token, refreshToken } = await issueTokens(userId, email, role);
99
+ async function setSessionCookies(c: any, userId: string, email: string, role: string, method: string, clientOrgId?: string | null) {
100
+ const { token, refreshToken } = await issueTokens(userId, email, role, clientOrgId);
98
101
  const csrf = generateCsrf();
99
102
  const secure = isSecure();
100
103
 
@@ -301,7 +304,7 @@ export function createAuthRoutes(
301
304
  });
302
305
  }
303
306
 
304
- const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'password');
307
+ const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'password', user.clientOrgId);
305
308
 
306
309
  return c.json({
307
310
  token,
@@ -360,7 +363,7 @@ export function createAuthRoutes(
360
363
 
361
364
  pending2fa.delete(challengeToken);
362
365
 
363
- const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'password+2fa');
366
+ const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'password+2fa', user.clientOrgId);
364
367
 
365
368
  return c.json({
366
369
  token,
@@ -614,7 +617,7 @@ export function createAuthRoutes(
614
617
  const user = await db.getUser(key.createdBy);
615
618
  if (!user) return c.json({ error: 'API key owner not found' }, 401);
616
619
 
617
- const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'api-key');
620
+ const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'api-key', user.clientOrgId);
618
621
 
619
622
  return c.json({
620
623
  token,
@@ -643,7 +646,7 @@ export function createAuthRoutes(
643
646
  const user = await db.getUser(payload.sub as string);
644
647
  if (!user) return c.json({ error: 'User not found' }, 401);
645
648
 
646
- const { token, refreshToken } = await issueTokens(user.id, user.email, user.role);
649
+ const { token, refreshToken } = await issueTokens(user.id, user.email, user.role, user.clientOrgId);
647
650
  const csrf = generateCsrf();
648
651
  const secure = isSecure();
649
652
 
@@ -694,7 +697,7 @@ export function createAuthRoutes(
694
697
  if (!target) return c.json({ error: 'User not found' }, 404);
695
698
 
696
699
  // Generate a short-lived token (1 hour) for the target user with impersonation flag
697
- const impersonateToken = await new SignJWT({ sub: target.id, role: target.role, impersonatedBy: caller.id })
700
+ const impersonateToken = await new SignJWT({ sub: target.id, role: target.role, impersonatedBy: caller.id, clientOrgId: target.clientOrgId || undefined, orgId: target.clientOrgId || (target as any).org_id || undefined })
698
701
  .setProtectedHeader({ alg: 'HS256' })
699
702
  .setIssuedAt()
700
703
  .setExpirationTime('1h')
@@ -890,7 +893,7 @@ export function createAuthRoutes(
890
893
  ip: c.req.header('x-forwarded-for') || c.req.header('x-real-ip'),
891
894
  });
892
895
 
893
- const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'bootstrap');
896
+ const { token, refreshToken, csrf } = await setSessionCookies(c, user.id, user.email, user.role, 'bootstrap', user.clientOrgId);
894
897
 
895
898
  // Notify server that setup is complete (flips the dashboard latch)
896
899
  opts?.onBootstrap?.();
@@ -1214,7 +1217,7 @@ export function createAuthRoutes(
1214
1217
  }
1215
1218
 
1216
1219
  // Issue session
1217
- await setSessionCookies(c, result.user.id, result.user.email, result.user.role, 'oidc');
1220
+ await setSessionCookies(c, result.user.id, result.user.email, result.user.role, 'oidc', result.user.clientOrgId);
1218
1221
 
1219
1222
  // Redirect to dashboard
1220
1223
  return c.redirect('/dashboard');
@@ -1363,7 +1366,7 @@ export function createAuthRoutes(
1363
1366
  }
1364
1367
 
1365
1368
  // Issue session
1366
- await setSessionCookies(c, result.user.id, result.user.email, result.user.role, 'saml');
1369
+ await setSessionCookies(c, result.user.id, result.user.email, result.user.role, 'saml', result.user.clientOrgId);
1367
1370
 
1368
1371
  // Redirect to dashboard (or RelayState)
1369
1372
  return c.redirect('/dashboard');
package/src/cli-agent.ts CHANGED
@@ -393,11 +393,8 @@ export async function runAgent(_args: string[]) {
393
393
  console.log(' Connecting to database...');
394
394
 
395
395
  // 1. Connect to shared enterprise DB
396
- const { createAdapter } = await import('./db/factory.js');
397
- const db = await createAdapter({
398
- type: DATABASE_URL.startsWith('postgres') ? 'postgres' : 'sqlite',
399
- connectionString: DATABASE_URL,
400
- });
396
+ const { createAdapter, smartDbConfig } = await import('./db/factory.js');
397
+ const db = await createAdapter(smartDbConfig(DATABASE_URL));
401
398
  await db.migrate();
402
399
 
403
400
  // 2. Initialize engine DB
@@ -902,6 +899,7 @@ export async function runAgent(_args: string[]) {
902
899
  isDM: boolean;
903
900
  messageText: string;
904
901
  isManager?: boolean;
902
+ mediaFiles?: Array<{ path: string; type: string; mimeType?: string }>;
905
903
  }>();
906
904
 
907
905
  const isMessagingSource = ['whatsapp', 'telegram'].includes(ctx.source);
@@ -1063,6 +1061,23 @@ export async function runAgent(_args: string[]) {
1063
1061
  `- No markdown formatting — plain text only.`,
1064
1062
  `- For simple greetings/questions, reply in ONE tool call. Do not overthink.`,
1065
1063
  '',
1064
+ `DEPENDENCY & TOOL MANAGEMENT:`,
1065
+ `- Before running commands that need specific tools (ffmpeg, imagemagick, etc.), use check_dependency to verify they're installed.`,
1066
+ `- If a tool is missing: use install_dependency to install it automatically (brew on macOS, apt on Linux).`,
1067
+ `- Tell your manager what you're installing and why — don't just silently install things. Example: "I need to install ffmpeg to process your video. Installing now..."`,
1068
+ `- After completing a task that required installing new tools, use cleanup_installed to review what was installed.`,
1069
+ `- For common tools (ffmpeg, imagemagick, jq, etc.) you can install without explicit permission — just inform.`,
1070
+ `- For unusual packages or large installs, ask your manager first.`,
1071
+ `- If installation fails, explain what happened and suggest alternatives.`,
1072
+ `- Use check_environment at the start of complex tasks to understand what's available.`,
1073
+ '',
1074
+ `FILE & MEDIA HANDLING:`,
1075
+ `- When you receive media files (images, videos, documents), they are saved locally and you can access them.`,
1076
+ `- For images: you can see them directly in the message. Describe what you see.`,
1077
+ `- For videos/audio: use ffmpeg (check_dependency first) to analyze, convert, or edit.`,
1078
+ `- For documents: use the appropriate tool to read/process them.`,
1079
+ `- You can send media back using ${ctx.source === 'telegram' ? 'telegram_send_media' : 'whatsapp_send_media'} with a local file path.`,
1080
+ '',
1066
1081
  buildScheduleInfo(agentSchedule, agentTimezone),
1067
1082
  ambientContext ? `\nCONTEXT FROM MEMORY:\n${ambientContext}` : '',
1068
1083
  ].filter(Boolean).join('\n');
@@ -1118,11 +1133,37 @@ export async function runAgent(_args: string[]) {
1118
1133
  });
1119
1134
  } catch (e: any) { /* non-fatal */ }
1120
1135
 
1136
+ // Build multimodal message content if media files are present
1137
+ let chatMessageContent: string = ctx.messageText;
1138
+ let mediaContentBlocks: any[] | undefined;
1139
+ if ((ctx as any).mediaFiles && (ctx as any).mediaFiles.length > 0) {
1140
+ const { readFileSync } = await import('fs');
1141
+ const blocks: any[] = [];
1142
+ if (ctx.messageText) blocks.push({ type: 'text', text: ctx.messageText });
1143
+ for (const media of (ctx as any).mediaFiles) {
1144
+ try {
1145
+ const buf = readFileSync(media.path);
1146
+ const b64 = buf.toString('base64');
1147
+ const mime = media.mimeType || (media.type === 'photo' ? 'image/jpeg' : 'application/octet-stream');
1148
+ if (mime.startsWith('image/')) {
1149
+ blocks.push({ type: 'image', source: { type: 'base64', media_type: mime, data: b64 } });
1150
+ blocks.push({ type: 'text', text: `[Image saved at: ${media.path}]` });
1151
+ } else {
1152
+ blocks.push({ type: 'text', text: `[File received: ${media.path} (${mime}). Use tools to read/process this file.]` });
1153
+ }
1154
+ } catch (fileErr: any) {
1155
+ blocks.push({ type: 'text', text: `[Media file: ${media.path} — could not read: ${fileErr.message}]` });
1156
+ }
1157
+ }
1158
+ if (blocks.length > 0) mediaContentBlocks = blocks;
1159
+ }
1160
+
1121
1161
  const session = await runtime.spawnSession({
1122
1162
  agentId: agentId,
1123
- message: ctx.messageText,
1163
+ message: chatMessageContent,
1124
1164
  systemPrompt,
1125
1165
  ...(sessionContext ? { sessionContext } : {}),
1166
+ ...(mediaContentBlocks ? { messageContent: mediaContentBlocks } : {}),
1126
1167
  });
1127
1168
 
1128
1169
  // Mark task as in progress
package/src/cli-serve.ts CHANGED
@@ -121,13 +121,10 @@ export async function runServe(_args: string[]) {
121
121
  process.exit(1);
122
122
  }
123
123
 
124
- const { createAdapter } = await import('./db/factory.js');
124
+ const { createAdapter, smartDbConfig } = await import('./db/factory.js');
125
125
  const { createServer } = await import('./server.js');
126
126
 
127
- const db = await createAdapter({
128
- type: DATABASE_URL.startsWith('postgres') ? 'postgres' : 'sqlite',
129
- connectionString: DATABASE_URL,
130
- });
127
+ const db = await createAdapter(smartDbConfig(DATABASE_URL));
131
128
 
132
129
  await db.migrate();
133
130