@hailer/mcp 1.1.11 → 1.1.13

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 (252) hide show
  1. package/dist/app.js +18 -5
  2. package/dist/bot/bot-config.d.ts +12 -1
  3. package/dist/bot/bot-config.js +98 -14
  4. package/dist/bot/bot-manager.d.ts +13 -3
  5. package/dist/bot/bot-manager.js +80 -25
  6. package/dist/bot/bot.d.ts +46 -0
  7. package/dist/bot/bot.js +542 -166
  8. package/dist/bot/services/message-classifier.js +17 -0
  9. package/dist/bot/services/permission-guard.d.ts +52 -0
  10. package/dist/bot/services/permission-guard.js +149 -0
  11. package/dist/bot/services/types.d.ts +5 -0
  12. package/dist/bot/services/typing-indicator.d.ts +6 -1
  13. package/dist/bot/services/typing-indicator.js +19 -3
  14. package/dist/config.d.ts +6 -1
  15. package/dist/config.js +43 -0
  16. package/dist/core.js +3 -6
  17. package/dist/mcp/UserContextCache.d.ts +5 -0
  18. package/dist/mcp/UserContextCache.js +51 -19
  19. package/dist/mcp/hailer-clients.d.ts +19 -1
  20. package/dist/mcp/hailer-clients.js +157 -20
  21. package/dist/mcp/session-store.d.ts +68 -0
  22. package/dist/mcp/session-store.js +169 -0
  23. package/dist/mcp/signal-handler.js +12 -12
  24. package/dist/mcp/tool-registry.d.ts +17 -4
  25. package/dist/mcp/tool-registry.js +37 -7
  26. package/dist/mcp/tools/activity.js +99 -7
  27. package/dist/mcp/tools/app-scaffold.js +304 -336
  28. package/dist/mcp/tools/company.d.ts +9 -0
  29. package/dist/mcp/tools/company.js +88 -0
  30. package/dist/mcp/tools/discussion.js +68 -0
  31. package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
  32. package/dist/mcp/tools/workflow-permissions.js +204 -0
  33. package/dist/mcp/tools/workflow.js +57 -18
  34. package/dist/mcp/utils/index.d.ts +2 -0
  35. package/dist/mcp/utils/index.js +12 -1
  36. package/dist/mcp/utils/role-utils.d.ts +74 -0
  37. package/dist/mcp/utils/role-utils.js +151 -0
  38. package/dist/mcp/utils/types.d.ts +43 -1
  39. package/dist/mcp/utils/types.js +14 -0
  40. package/dist/mcp/webhook-handler.d.ts +6 -0
  41. package/dist/mcp/webhook-handler.js +11 -0
  42. package/dist/mcp-server.d.ts +23 -2
  43. package/dist/mcp-server.js +639 -111
  44. package/dist/plugins/vipunen/client.d.ts +150 -0
  45. package/dist/plugins/vipunen/client.js +535 -0
  46. package/dist/plugins/vipunen/config/schema-config.json +19 -0
  47. package/dist/plugins/vipunen/config/schema-doc.json +22 -0
  48. package/dist/plugins/vipunen/index.d.ts +41 -0
  49. package/dist/plugins/vipunen/index.js +88 -0
  50. package/dist/plugins/vipunen/tools.d.ts +26 -0
  51. package/dist/plugins/vipunen/tools.js +501 -0
  52. package/package.json +2 -1
  53. package/.claude/.context-watchdog.json +0 -1
  54. package/.claude/.session-checked +0 -1
  55. package/.claude/CLAUDE.md +0 -370
  56. package/.claude/agents/agent-ada-skill-builder.md +0 -94
  57. package/.claude/agents/agent-alejandro-function-fields.md +0 -342
  58. package/.claude/agents/agent-bjorn-config-audit.md +0 -103
  59. package/.claude/agents/agent-builder-agent-creator.md +0 -130
  60. package/.claude/agents/agent-code-simplifier.md +0 -53
  61. package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
  62. package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
  63. package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
  64. package/.claude/agents/agent-helga-workflow-config.md +0 -204
  65. package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
  66. package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
  67. package/.claude/agents/agent-ivan-monolith.md +0 -154
  68. package/.claude/agents/agent-kenji-data-reader.md +0 -86
  69. package/.claude/agents/agent-lars-code-inspector.md +0 -102
  70. package/.claude/agents/agent-marco-mockup-builder.md +0 -110
  71. package/.claude/agents/agent-marcus-api-documenter.md +0 -323
  72. package/.claude/agents/agent-marketplace-publisher.md +0 -280
  73. package/.claude/agents/agent-marketplace-reviewer.md +0 -309
  74. package/.claude/agents/agent-permissions-handler.md +0 -208
  75. package/.claude/agents/agent-simple-writer.md +0 -48
  76. package/.claude/agents/agent-svetlana-code-review.md +0 -171
  77. package/.claude/agents/agent-tanya-test-runner.md +0 -333
  78. package/.claude/agents/agent-ui-designer.md +0 -100
  79. package/.claude/agents/agent-viktor-sql-insights.md +0 -212
  80. package/.claude/agents/agent-web-search.md +0 -55
  81. package/.claude/agents/agent-yevgeni-discussions.md +0 -45
  82. package/.claude/agents/agent-zara-zapier.md +0 -159
  83. package/.claude/commands/app-squad.md +0 -135
  84. package/.claude/commands/audit-squad.md +0 -158
  85. package/.claude/commands/autoplan.md +0 -563
  86. package/.claude/commands/cleanup-squad.md +0 -98
  87. package/.claude/commands/config-squad.md +0 -106
  88. package/.claude/commands/crud-squad.md +0 -87
  89. package/.claude/commands/data-squad.md +0 -97
  90. package/.claude/commands/debug-squad.md +0 -303
  91. package/.claude/commands/doc-squad.md +0 -65
  92. package/.claude/commands/handoff.md +0 -137
  93. package/.claude/commands/health.md +0 -49
  94. package/.claude/commands/help.md +0 -29
  95. package/.claude/commands/help:agents.md +0 -151
  96. package/.claude/commands/help:commands.md +0 -78
  97. package/.claude/commands/help:faq.md +0 -79
  98. package/.claude/commands/help:plugins.md +0 -50
  99. package/.claude/commands/help:skills.md +0 -93
  100. package/.claude/commands/help:tools.md +0 -75
  101. package/.claude/commands/hotfix-squad.md +0 -112
  102. package/.claude/commands/integration-squad.md +0 -82
  103. package/.claude/commands/janitor-squad.md +0 -167
  104. package/.claude/commands/learn-auto.md +0 -120
  105. package/.claude/commands/learn.md +0 -120
  106. package/.claude/commands/mcp-list.md +0 -27
  107. package/.claude/commands/onboard-squad.md +0 -140
  108. package/.claude/commands/plan-workspace.md +0 -732
  109. package/.claude/commands/prd.md +0 -130
  110. package/.claude/commands/project-status.md +0 -82
  111. package/.claude/commands/publish.md +0 -138
  112. package/.claude/commands/recap.md +0 -69
  113. package/.claude/commands/restore.md +0 -64
  114. package/.claude/commands/review-squad.md +0 -152
  115. package/.claude/commands/save.md +0 -24
  116. package/.claude/commands/stats.md +0 -19
  117. package/.claude/commands/swarm.md +0 -210
  118. package/.claude/commands/tool-builder.md +0 -39
  119. package/.claude/commands/ws-pull.md +0 -44
  120. package/.claude/hooks/_shared-memory.cjs +0 -305
  121. package/.claude/hooks/_utils.cjs +0 -108
  122. package/.claude/hooks/agent-failure-detector.cjs +0 -383
  123. package/.claude/hooks/agent-usage-logger.cjs +0 -204
  124. package/.claude/hooks/app-edit-guard.cjs +0 -494
  125. package/.claude/hooks/auto-learn.cjs +0 -304
  126. package/.claude/hooks/bash-guard.cjs +0 -272
  127. package/.claude/hooks/builder-mode-manager.cjs +0 -354
  128. package/.claude/hooks/bulk-activity-guard.cjs +0 -271
  129. package/.claude/hooks/context-watchdog.cjs +0 -230
  130. package/.claude/hooks/delegation-reminder.cjs +0 -465
  131. package/.claude/hooks/design-system-lint.cjs +0 -271
  132. package/.claude/hooks/post-scaffold-hook.cjs +0 -181
  133. package/.claude/hooks/prompt-guard.cjs +0 -354
  134. package/.claude/hooks/publish-template-guard.cjs +0 -147
  135. package/.claude/hooks/session-start.cjs +0 -35
  136. package/.claude/hooks/shared-memory-writer.cjs +0 -147
  137. package/.claude/hooks/skill-injector.cjs +0 -140
  138. package/.claude/hooks/skill-usage-logger.cjs +0 -258
  139. package/.claude/hooks/src-edit-guard.cjs +0 -240
  140. package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
  141. package/.claude/settings.json +0 -257
  142. package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
  143. package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
  144. package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
  145. package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
  146. package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
  147. package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
  148. package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
  149. package/.claude/skills/agent-structure/SKILL.md +0 -98
  150. package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
  151. package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
  152. package/.claude/skills/delegation-routing/SKILL.md +0 -202
  153. package/.claude/skills/frontend-design/SKILL.md +0 -254
  154. package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
  155. package/.claude/skills/hailer-api-client/SKILL.md +0 -518
  156. package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
  157. package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
  158. package/.claude/skills/hailer-design-system/SKILL.md +0 -235
  159. package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
  160. package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
  161. package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
  162. package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
  163. package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
  164. package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
  165. package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
  166. package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
  167. package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
  168. package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
  169. package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
  170. package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
  171. package/.claude/skills/integration-patterns/SKILL.md +0 -421
  172. package/.claude/skills/json-only-output/SKILL.md +0 -72
  173. package/.claude/skills/lsp-setup/SKILL.md +0 -160
  174. package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
  175. package/.claude/skills/optional-parameters/SKILL.md +0 -72
  176. package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
  177. package/.claude/skills/testing-patterns/SKILL.md +0 -630
  178. package/.claude/skills/tool-builder/SKILL.md +0 -250
  179. package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
  180. package/.claude/skills/tool-response-verification/SKILL.md +0 -92
  181. package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
  182. package/.hailer-mcp-port +0 -1
  183. package/.mcp.json +0 -13
  184. package/.opencode/agent/agent-ada-skill-builder.md +0 -35
  185. package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
  186. package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
  187. package/.opencode/agent/agent-builder-agent-creator.md +0 -39
  188. package/.opencode/agent/agent-code-simplifier.md +0 -31
  189. package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
  190. package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
  191. package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
  192. package/.opencode/agent/agent-helga-workflow-config.md +0 -204
  193. package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
  194. package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
  195. package/.opencode/agent/agent-ivan-monolith.md +0 -46
  196. package/.opencode/agent/agent-kenji-data-reader.md +0 -53
  197. package/.opencode/agent/agent-lars-code-inspector.md +0 -28
  198. package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
  199. package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
  200. package/.opencode/agent/agent-marketplace-publisher.md +0 -44
  201. package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
  202. package/.opencode/agent/agent-permissions-handler.md +0 -50
  203. package/.opencode/agent/agent-simple-writer.md +0 -45
  204. package/.opencode/agent/agent-svetlana-code-review.md +0 -39
  205. package/.opencode/agent/agent-tanya-test-runner.md +0 -57
  206. package/.opencode/agent/agent-ui-designer.md +0 -56
  207. package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
  208. package/.opencode/agent/agent-web-search.md +0 -42
  209. package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
  210. package/.opencode/agent/agent-zara-zapier.md +0 -53
  211. package/.opencode/commands/app-squad.md +0 -135
  212. package/.opencode/commands/audit-squad.md +0 -158
  213. package/.opencode/commands/autoplan.md +0 -563
  214. package/.opencode/commands/cleanup-squad.md +0 -98
  215. package/.opencode/commands/config-squad.md +0 -106
  216. package/.opencode/commands/crud-squad.md +0 -87
  217. package/.opencode/commands/data-squad.md +0 -97
  218. package/.opencode/commands/debug-squad.md +0 -303
  219. package/.opencode/commands/doc-squad.md +0 -65
  220. package/.opencode/commands/handoff.md +0 -137
  221. package/.opencode/commands/health.md +0 -49
  222. package/.opencode/commands/help-agents.md +0 -151
  223. package/.opencode/commands/help-commands.md +0 -32
  224. package/.opencode/commands/help-faq.md +0 -29
  225. package/.opencode/commands/help-plugins.md +0 -28
  226. package/.opencode/commands/help-skills.md +0 -7
  227. package/.opencode/commands/help-tools.md +0 -40
  228. package/.opencode/commands/help.md +0 -28
  229. package/.opencode/commands/hotfix-squad.md +0 -112
  230. package/.opencode/commands/integration-squad.md +0 -82
  231. package/.opencode/commands/janitor-squad.md +0 -167
  232. package/.opencode/commands/learn-auto.md +0 -120
  233. package/.opencode/commands/learn.md +0 -120
  234. package/.opencode/commands/mcp-list.md +0 -27
  235. package/.opencode/commands/onboard-squad.md +0 -140
  236. package/.opencode/commands/plan-workspace.md +0 -732
  237. package/.opencode/commands/prd.md +0 -131
  238. package/.opencode/commands/project-status.md +0 -82
  239. package/.opencode/commands/publish.md +0 -138
  240. package/.opencode/commands/recap.md +0 -69
  241. package/.opencode/commands/restore.md +0 -64
  242. package/.opencode/commands/review-squad.md +0 -152
  243. package/.opencode/commands/save.md +0 -24
  244. package/.opencode/commands/stats.md +0 -19
  245. package/.opencode/commands/swarm.md +0 -210
  246. package/.opencode/commands/tool-builder.md +0 -39
  247. package/.opencode/commands/ws-pull.md +0 -44
  248. package/.opencode/opencode.json +0 -21
  249. package/inbox/failures.log +0 -1
  250. package/inbox/usage.jsonl +0 -4
  251. package/scripts/postinstall.cjs +0 -64
  252. package/scripts/test-hal-tools.ts +0 -154
package/dist/app.js CHANGED
@@ -8,9 +8,13 @@ const file_1 = require("./mcp/tools/file");
8
8
  const activity_1 = require("./mcp/tools/activity");
9
9
  const discussion_1 = require("./mcp/tools/discussion");
10
10
  const user_1 = require("./mcp/tools/user");
11
+ const company_1 = require("./mcp/tools/company");
11
12
  const workflow_1 = require("./mcp/tools/workflow");
12
13
  const insight_1 = require("./mcp/tools/insight");
14
+ const workflow_permissions_1 = require("./mcp/tools/workflow-permissions");
13
15
  const app_1 = require("./mcp/tools/app");
16
+ // Vipunen plugin — Weaviate RAG knowledge base (conditional on WEAVIATE_HOST)
17
+ const vipunen_1 = require("./plugins/vipunen");
14
18
  // Bot config and bug-fixer tools are dynamically imported only when MCP_CLIENT_ENABLED=true
15
19
  // This allows the npm package to work without these modules
16
20
  const core = new core_1.Core();
@@ -22,9 +26,6 @@ core.addTool(activity_1.listActivitiesTool);
22
26
  core.addTool(activity_1.showActivityByIdTool);
23
27
  core.addTool(activity_1.createActivityTool);
24
28
  core.addTool(activity_1.updateActivityTool);
25
- // core.addTool(searchActivitiesTool);
26
- // core.addTool(filterActivitiesTool);
27
- // core.addTool(activityToolsGuideTool);
28
29
  core.addTool(discussion_1.listMyDiscussionsTool);
29
30
  core.addTool(discussion_1.fetchDiscussionMessagesTool);
30
31
  core.addTool(discussion_1.fetchPreviousDiscussionMessagesTool);
@@ -35,8 +36,7 @@ core.addTool(discussion_1.inviteDiscussionMembersTool);
35
36
  core.addTool(discussion_1.getActivityFromDiscussionTool);
36
37
  core.addTool(user_1.searchWorkspaceUsersTool);
37
38
  core.addTool(user_1.getWorkspaceBalanceTool);
38
- // core.addTool(hailerGetInitTool);
39
- // core.addTool(validateUserIdTool);
39
+ core.addTool(company_1.setUserRoleTool);
40
40
  core.addTool(workflow_1.getWorkflowSchemaTool);
41
41
  core.addTool(workflow_1.listWorkflowPhasesTool);
42
42
  core.addTool(workflow_1.listWorkflowsTool);
@@ -50,6 +50,10 @@ core.addTool(workflow_1.testFunctionFieldTool);
50
50
  core.addTool(workflow_1.listWorkflowsMinimalTool);
51
51
  core.addTool(workflow_1.countActivitiesTool);
52
52
  core.addTool(workflow_1.coreInitTool);
53
+ core.addTool(workflow_permissions_1.listWorkflowPermissionsTool);
54
+ core.addTool(workflow_permissions_1.grantWorkflowPermissionTool);
55
+ core.addTool(workflow_permissions_1.revokeWorkflowPermissionTool);
56
+ core.addTool(workflow_permissions_1.checkUserPermissionsTool);
53
57
  core.addTool(insight_1.createInsightTool);
54
58
  core.addTool(insight_1.previewInsightTool);
55
59
  core.addTool(insight_1.getInsightDataTool);
@@ -79,6 +83,15 @@ core.addTool(app_1.getProductManifestTool);
79
83
  // Marketplace app tools
80
84
  core.addTool(app_1.publishAppTool);
81
85
  core.addTool(app_1.installMarketplaceAppTool);
86
+ // Vipunen tools (Weaviate RAG) — only registered when WEAVIATE_HOST is set
87
+ const vipunenTools = (0, vipunen_1.registerVipunenTools)({
88
+ weaviateHost: config_1.environment.WEAVIATE_HOST,
89
+ weaviateApiKey: config_1.environment.WEAVIATE_API_KEY,
90
+ vipunenApiKeys: config_1.environment.VIPUNEN_API_KEYS,
91
+ });
92
+ for (const tool of vipunenTools) {
93
+ core.addTool(tool);
94
+ }
82
95
  // Bot-internal tools - dynamically imported only when MCP_CLIENT_ENABLED=true
83
96
  // Wrapped in try/catch so global installs work (bot-config excluded from npm package)
84
97
  if (config_1.environment.MCP_CLIENT_ENABLED) {
@@ -4,18 +4,29 @@
4
4
  * Simple config reader for .bot-config/*.json files.
5
5
  * Extracts orchestrator credentials into a flat BotConfig interface.
6
6
  */
7
+ import { ResponseMode } from './services/types';
8
+ /** Who can talk to this bot */
9
+ export type BotAccessLevel = 'all' | 'admin' | 'owner';
7
10
  export interface BotConfig {
11
+ activityId: string;
8
12
  workspaceId: string;
9
13
  workspaceName: string;
10
14
  email: string;
11
15
  password: string;
12
16
  displayName?: string;
17
+ systemPrompt?: string;
13
18
  enabled: boolean;
14
19
  apiHost: string;
20
+ /** Who can talk to this bot: 'all' (default), 'admin', or 'owner' */
21
+ accessLevel?: BotAccessLevel;
22
+ /** Workflow IDs the bot is allowed to access. Empty/undefined = all workflows. */
23
+ allowedWorkflows?: string[];
24
+ /** When the bot responds: 'always' (default), 'mention_only', 'reply_only', 'mention_or_reply' */
25
+ responseMode?: ResponseMode;
15
26
  }
16
27
  /**
17
28
  * Load all bot configs from .bot-config/*.json files.
18
- * Extracts orchestrator credentials into flat BotConfig objects.
29
+ * Falls back to BOT_EMAIL/BOT_PASSWORD or BOT_ACCOUNTS env vars.
19
30
  */
20
31
  export declare function loadBotConfigs(): BotConfig[];
21
32
  /**
@@ -52,15 +52,66 @@ function getApiHost() {
52
52
  function getConfigDir() {
53
53
  return path.join(process.cwd(), '.bot-config');
54
54
  }
55
+ /**
56
+ * Load bot configs from env vars (BOT_EMAIL/BOT_PASSWORD or BOT_ACCOUNTS).
57
+ * Returns empty array if no env vars are set.
58
+ */
59
+ function loadEnvBotConfigs() {
60
+ // Multi-bot: BOT_ACCOUNTS JSON array
61
+ const botAccountsStr = process.env.BOT_ACCOUNTS;
62
+ if (botAccountsStr) {
63
+ try {
64
+ const accounts = JSON.parse(botAccountsStr);
65
+ const configs = accounts
66
+ .filter(a => a.email && a.password)
67
+ .map(a => ({
68
+ activityId: '',
69
+ workspaceId: 'env',
70
+ workspaceName: 'env',
71
+ email: a.email,
72
+ password: a.password,
73
+ displayName: a.displayName || process.env.BOT_DISPLAY_NAME || 'Dev AI',
74
+ enabled: true,
75
+ apiHost: getApiHost(),
76
+ }));
77
+ if (configs.length > 0) {
78
+ logger.info('Loaded bot configs from BOT_ACCOUNTS env', { count: configs.length });
79
+ return configs;
80
+ }
81
+ }
82
+ catch (error) {
83
+ logger.warn('Failed to parse BOT_ACCOUNTS env var', {
84
+ error: error instanceof Error ? error.message : String(error),
85
+ });
86
+ }
87
+ }
88
+ // Single bot: BOT_EMAIL + BOT_PASSWORD
89
+ const email = process.env.BOT_EMAIL;
90
+ const password = process.env.BOT_PASSWORD;
91
+ if (email && password) {
92
+ logger.info('Loaded bot config from BOT_EMAIL/BOT_PASSWORD env');
93
+ return [{
94
+ activityId: '',
95
+ workspaceId: 'env',
96
+ workspaceName: 'env',
97
+ email,
98
+ password,
99
+ displayName: process.env.BOT_DISPLAY_NAME || 'Dev AI',
100
+ enabled: true,
101
+ apiHost: getApiHost(),
102
+ }];
103
+ }
104
+ return [];
105
+ }
55
106
  /**
56
107
  * Load all bot configs from .bot-config/*.json files.
57
- * Extracts orchestrator credentials into flat BotConfig objects.
108
+ * Falls back to BOT_EMAIL/BOT_PASSWORD or BOT_ACCOUNTS env vars.
58
109
  */
59
110
  function loadBotConfigs() {
60
111
  const configDir = getConfigDir();
61
112
  if (!fs.existsSync(configDir)) {
62
- logger.warn('No .bot-config directory found');
63
- return [];
113
+ logger.debug('No .bot-config directory found, checking env vars');
114
+ return loadEnvBotConfigs();
64
115
  }
65
116
  const files = fs.readdirSync(configDir).filter(f => f.endsWith('.json'));
66
117
  const configs = [];
@@ -68,21 +119,46 @@ function loadBotConfigs() {
68
119
  try {
69
120
  const filePath = path.join(configDir, file);
70
121
  const raw = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
122
+ // Load orchestrator
71
123
  const orch = raw.orchestrator;
72
- const hasCredentials = !!orch?.email && !!orch?.password;
73
- configs.push({
74
- workspaceId: raw.workspaceId,
75
- workspaceName: raw.workspaceName,
76
- email: orch?.email ?? '',
77
- password: orch?.password ?? '',
78
- displayName: orch?.displayName,
79
- enabled: hasCredentials,
80
- apiHost: getApiHost(),
81
- });
124
+ if (orch?.email && orch?.password) {
125
+ configs.push({
126
+ activityId: orch.activityId || '',
127
+ workspaceId: raw.workspaceId,
128
+ workspaceName: raw.workspaceName,
129
+ email: orch.email,
130
+ password: orch.password,
131
+ displayName: orch.displayName,
132
+ systemPrompt: orch.systemPrompt,
133
+ enabled: true,
134
+ apiHost: getApiHost(),
135
+ accessLevel: orch.accessLevel,
136
+ allowedWorkflows: orch.allowedWorkflows,
137
+ responseMode: orch.responseMode,
138
+ });
139
+ }
140
+ // Load specialists
141
+ if (Array.isArray(raw.specialists)) {
142
+ for (const spec of raw.specialists) {
143
+ if (spec?.email && spec?.password && spec?.enabled !== false) {
144
+ configs.push({
145
+ activityId: spec.activityId || '',
146
+ workspaceId: raw.workspaceId,
147
+ workspaceName: raw.workspaceName,
148
+ email: spec.email,
149
+ password: spec.password,
150
+ displayName: spec.displayName,
151
+ systemPrompt: spec.systemPrompt,
152
+ enabled: true,
153
+ apiHost: getApiHost(),
154
+ });
155
+ }
156
+ }
157
+ }
82
158
  logger.debug('Loaded bot config', {
83
159
  workspaceId: raw.workspaceId,
84
160
  workspaceName: raw.workspaceName,
85
- enabled: hasCredentials,
161
+ botCount: configs.filter(c => c.workspaceId === raw.workspaceId).length,
86
162
  });
87
163
  }
88
164
  catch (error) {
@@ -92,6 +168,11 @@ function loadBotConfigs() {
92
168
  });
93
169
  }
94
170
  }
171
+ // If no configs from files, fall back to env vars
172
+ if (configs.length === 0) {
173
+ logger.debug('No bot configs from files, checking env vars');
174
+ return loadEnvBotConfigs();
175
+ }
95
176
  logger.debug('Bot configs loaded', { total: configs.length });
96
177
  return configs;
97
178
  }
@@ -124,6 +205,9 @@ function saveBotConfig(config) {
124
205
  email: config.email,
125
206
  password: config.password,
126
207
  displayName: config.displayName,
208
+ systemPrompt: config.systemPrompt,
209
+ accessLevel: config.accessLevel,
210
+ allowedWorkflows: config.allowedWorkflows,
127
211
  } : null,
128
212
  specialists: existing.specialists ?? [],
129
213
  lastSynced: new Date().toISOString(),
@@ -7,23 +7,33 @@
7
7
  import { ToolRegistry } from '../mcp/tool-registry';
8
8
  import { BotConfig } from './bot-config';
9
9
  interface BotUpdateEntry {
10
+ activityId: string;
10
11
  email: string;
11
12
  password: string;
12
13
  botType: string;
13
14
  enabled: boolean;
14
15
  displayName?: string;
16
+ systemPrompt?: string;
17
+ accessLevel?: string;
18
+ responseMode?: string;
15
19
  }
16
20
  export declare class BotManager {
17
21
  private toolRegistry;
18
22
  private anthropicApiKey;
23
+ /** Key: activityId → Bot instance */
19
24
  private bots;
25
+ /** Key: workspaceId → Set of activityIds (for cross-bot awareness) */
26
+ private workspaceBots;
20
27
  private apiHost;
21
28
  constructor(toolRegistry: ToolRegistry, anthropicApiKey: string);
29
+ /** Get all bot userIds in a workspace (for self-message guard) */
30
+ getBotUserIdsForWorkspace(workspaceId: string): Set<string>;
22
31
  startAll(): Promise<void>;
23
32
  startBot(config: BotConfig): Promise<void>;
24
- stopBot(workspaceId: string): Promise<void>;
33
+ stopBot(botKey: string, workspaceId?: string): Promise<void>;
25
34
  stopAll(): Promise<void>;
26
35
  getStatus(): Array<{
36
+ activityId: string;
27
37
  workspaceId: string;
28
38
  connected: boolean;
29
39
  displayName: string;
@@ -31,8 +41,8 @@ export declare class BotManager {
31
41
  getBotCount(): number;
32
42
  /**
33
43
  * Handle hot reload from webhook updates.
34
- * Only processes orchestrator bots. Workspace-isolated only touches
35
- * the bot for the specified workspaceId, never affects other workspaces.
44
+ * Supports all bot types. Routes by activityId so multiple bots
45
+ * can run in the same workspace independently.
36
46
  */
37
47
  handleBotUpdate(workspaceId: string, bot: BotUpdateEntry, action: 'add' | 'update' | 'remove'): Promise<void>;
38
48
  }
@@ -14,13 +14,29 @@ const logger = (0, logger_1.createLogger)({ component: 'bot-manager' });
14
14
  class BotManager {
15
15
  toolRegistry;
16
16
  anthropicApiKey;
17
+ /** Key: activityId → Bot instance */
17
18
  bots = new Map();
19
+ /** Key: workspaceId → Set of activityIds (for cross-bot awareness) */
20
+ workspaceBots = new Map();
18
21
  apiHost;
19
22
  constructor(toolRegistry, anthropicApiKey) {
20
23
  this.toolRegistry = toolRegistry;
21
24
  this.anthropicApiKey = anthropicApiKey;
22
25
  this.apiHost = process.env.BOT_API_BASE_URL || 'https://api.hailer.com';
23
26
  }
27
+ /** Get all bot userIds in a workspace (for self-message guard) */
28
+ getBotUserIdsForWorkspace(workspaceId) {
29
+ const ids = new Set();
30
+ const activityIds = this.workspaceBots.get(workspaceId);
31
+ if (activityIds) {
32
+ for (const actId of activityIds) {
33
+ const bot = this.bots.get(actId);
34
+ if (bot?.botUserId)
35
+ ids.add(bot.botUserId);
36
+ }
37
+ }
38
+ return ids;
39
+ }
24
40
  async startAll() {
25
41
  const configs = (0, bot_config_1.loadBotConfigs)();
26
42
  const enabled = configs.filter(c => c.enabled);
@@ -37,11 +53,13 @@ class BotManager {
37
53
  logger.debug('Bot startup complete', { succeeded, failed });
38
54
  }
39
55
  async startBot(config) {
40
- if (this.bots.has(config.workspaceId)) {
41
- logger.warn('Bot already running', { workspaceId: config.workspaceId });
56
+ const botKey = config.activityId || config.workspaceId; // fallback for legacy configs
57
+ if (this.bots.has(botKey)) {
58
+ logger.warn('Bot already running', { activityId: botKey, workspaceId: config.workspaceId });
42
59
  return;
43
60
  }
44
61
  logger.debug('Starting bot', {
62
+ activityId: botKey,
45
63
  workspaceId: config.workspaceId,
46
64
  workspace: config.workspaceName,
47
65
  });
@@ -52,32 +70,53 @@ class BotManager {
52
70
  anthropicApiKey: this.anthropicApiKey,
53
71
  toolRegistry: this.toolRegistry,
54
72
  workspaceId: config.workspaceId,
73
+ systemPrompt: config.systemPrompt,
74
+ responseMode: config.responseMode,
75
+ botManager: this,
76
+ accessLevel: config.accessLevel,
77
+ allowedWorkflows: config.allowedWorkflows,
55
78
  });
56
79
  await bot.start();
57
- this.bots.set(config.workspaceId, bot);
58
- logger.debug('Bot started', { workspaceId: config.workspaceId });
80
+ this.bots.set(botKey, bot);
81
+ // Track in workspace lookup
82
+ if (!this.workspaceBots.has(config.workspaceId)) {
83
+ this.workspaceBots.set(config.workspaceId, new Set());
84
+ }
85
+ this.workspaceBots.get(config.workspaceId).add(botKey);
86
+ logger.debug('Bot started', { activityId: botKey, workspaceId: config.workspaceId });
59
87
  }
60
- async stopBot(workspaceId) {
61
- const bot = this.bots.get(workspaceId);
88
+ async stopBot(botKey, workspaceId) {
89
+ const bot = this.bots.get(botKey);
62
90
  if (!bot)
63
91
  return;
64
92
  await bot.stop();
65
- this.bots.delete(workspaceId);
66
- logger.debug('Bot stopped', { workspaceId });
93
+ this.bots.delete(botKey);
94
+ // Clean up workspace tracking
95
+ if (workspaceId) {
96
+ const wsSet = this.workspaceBots.get(workspaceId);
97
+ if (wsSet) {
98
+ wsSet.delete(botKey);
99
+ if (wsSet.size === 0)
100
+ this.workspaceBots.delete(workspaceId);
101
+ }
102
+ }
103
+ logger.debug('Bot stopped', { activityId: botKey, workspaceId });
67
104
  }
68
105
  async stopAll() {
69
106
  logger.debug('Stopping all bots', { count: this.bots.size });
70
- await Promise.allSettled(Array.from(this.bots.entries()).map(async ([wsId, bot]) => {
107
+ await Promise.allSettled(Array.from(this.bots.entries()).map(async ([botKey, bot]) => {
71
108
  await bot.stop();
72
- logger.debug('Bot stopped', { workspaceId: wsId });
109
+ logger.debug('Bot stopped', { activityId: botKey });
73
110
  }));
74
111
  this.bots.clear();
112
+ this.workspaceBots.clear();
75
113
  }
76
114
  getStatus() {
77
- return Array.from(this.bots.entries()).map(([wsId, bot]) => ({
78
- workspaceId: wsId,
115
+ return Array.from(this.bots.entries()).map(([botKey, bot]) => ({
116
+ activityId: botKey,
117
+ workspaceId: bot.workspaceId || botKey,
79
118
  connected: bot.connected,
80
- displayName: wsId,
119
+ displayName: bot.workspaceId || botKey,
81
120
  }));
82
121
  }
83
122
  getBotCount() {
@@ -85,48 +124,64 @@ class BotManager {
85
124
  }
86
125
  /**
87
126
  * Handle hot reload from webhook updates.
88
- * Only processes orchestrator bots. Workspace-isolated only touches
89
- * the bot for the specified workspaceId, never affects other workspaces.
127
+ * Supports all bot types. Routes by activityId so multiple bots
128
+ * can run in the same workspace independently.
90
129
  */
91
130
  async handleBotUpdate(workspaceId, bot, action) {
92
- if (bot.botType !== 'orchestrator') {
93
- logger.debug('Ignoring non-orchestrator bot update', { workspaceId, botType: bot.botType });
131
+ const botKey = bot.activityId;
132
+ if (!botKey) {
133
+ logger.warn('Bot update missing activityId, skipping', { workspaceId });
94
134
  return;
95
135
  }
96
- const running = this.bots.has(workspaceId);
136
+ const running = this.bots.has(botKey);
97
137
  if (action === 'remove' || !bot.enabled) {
98
138
  if (running) {
99
- logger.info('Stopping bot (disabled via webhook)', { workspaceId });
100
- await this.stopBot(workspaceId);
139
+ logger.info('Stopping bot (disabled via webhook)', { activityId: botKey, workspaceId });
140
+ await this.stopBot(botKey, workspaceId);
101
141
  }
102
142
  return;
103
143
  }
104
144
  // Validate credentials before proceeding
105
145
  if (!/.+@.+\..+/.test(bot.email) || !bot.password || bot.password.length < 6) {
106
- logger.warn('Invalid bot credentials, skipping', { workspaceId });
146
+ logger.warn('Invalid bot credentials, skipping', { activityId: botKey, workspaceId });
107
147
  return;
108
148
  }
109
149
  // action is 'add' or 'update' with enabled=true
110
150
  if (running) {
111
- // Restart credentials or config may have changed
112
- logger.info('Restarting bot (updated via webhook)', { workspaceId });
113
- await this.stopBot(workspaceId);
151
+ const runningBot = this.bots.get(botKey);
152
+ // If only the system prompt changed, update it live — no restart needed
153
+ const credentialsChanged = runningBot.email !== bot.email || runningBot.password !== bot.password;
154
+ const accessLevelChanged = bot.accessLevel !== undefined && runningBot.accessLevel !== bot.accessLevel;
155
+ if (!credentialsChanged && !accessLevelChanged) {
156
+ logger.info('Updating bot config live (no restart needed)', { activityId: botKey, workspaceId });
157
+ runningBot.updateSystemPrompt(bot.systemPrompt);
158
+ runningBot.updateResponseMode(bot.responseMode);
159
+ return;
160
+ }
161
+ // Credentials changed — full restart required
162
+ logger.info('Restarting bot (credentials updated via webhook)', { activityId: botKey, workspaceId });
163
+ await this.stopBot(botKey, workspaceId);
114
164
  }
115
165
  const config = {
166
+ activityId: botKey,
116
167
  workspaceId,
117
168
  workspaceName: workspaceId,
118
169
  email: bot.email,
119
170
  password: bot.password,
120
171
  displayName: bot.displayName,
172
+ systemPrompt: bot.systemPrompt,
121
173
  enabled: true,
122
174
  apiHost: this.apiHost,
175
+ accessLevel: bot.accessLevel,
176
+ responseMode: bot.responseMode,
123
177
  };
124
178
  try {
125
179
  await this.startBot(config);
126
- logger.info('Bot started via webhook', { workspaceId, displayName: bot.displayName });
180
+ logger.info('Bot started via webhook', { activityId: botKey, workspaceId, displayName: bot.displayName });
127
181
  }
128
182
  catch (error) {
129
183
  logger.error('Failed to start bot via webhook', {
184
+ activityId: botKey,
130
185
  workspaceId,
131
186
  error: error instanceof Error ? error.message : String(error),
132
187
  });
package/dist/bot/bot.d.ts CHANGED
@@ -6,6 +6,8 @@
6
6
  * and posts responses back to discussions.
7
7
  */
8
8
  import { ToolRegistry } from '../mcp/tool-registry';
9
+ import { ResponseMode } from './services/types';
10
+ import { BotAccessLevel } from './bot-config';
9
11
  export declare class Bot {
10
12
  private logger;
11
13
  private clientManager;
@@ -18,12 +20,17 @@ export declare class Bot {
18
20
  private workspaceOverview;
19
21
  private userId;
20
22
  private _workspaceId;
23
+ private _systemPrompt;
24
+ private _responseMode;
21
25
  private conversationManager;
22
26
  private messageClassifier;
23
27
  private messageFormatter;
24
28
  private typingIndicator;
25
29
  private tokenBilling;
26
30
  private sessionLogger;
31
+ private hailerApi;
32
+ /** Cache of insightId → Set<workflowId> for permission checks on ID-based insight tools */
33
+ private insightWorkflowCache;
27
34
  private opLogger;
28
35
  private discussionStates;
29
36
  private processingDiscussions;
@@ -44,7 +51,10 @@ export declare class Bot {
44
51
  private permissionIndex;
45
52
  private adminUserIds;
46
53
  private ownerUserIds;
54
+ private workspaceMemberIds;
55
+ private membersLoaded;
47
56
  private config;
57
+ private botManager;
48
58
  constructor(config: {
49
59
  email: string;
50
60
  password: string;
@@ -53,11 +63,28 @@ export declare class Bot {
53
63
  model?: string;
54
64
  toolRegistry: ToolRegistry;
55
65
  workspaceId?: string;
66
+ systemPrompt?: string;
67
+ responseMode?: ResponseMode;
68
+ botManager?: any;
69
+ accessLevel?: BotAccessLevel;
70
+ allowedWorkflows?: string[];
56
71
  });
72
+ get email(): string;
73
+ get password(): string;
74
+ get accessLevel(): string;
75
+ /**
76
+ * Hot-update the system prompt without restarting the bot.
77
+ * Takes effect on the next message processed.
78
+ */
79
+ updateSystemPrompt(prompt: string | undefined): void;
80
+ updateResponseMode(mode: ResponseMode | undefined): void;
57
81
  get connected(): boolean;
58
82
  get workspaceId(): string | undefined;
83
+ /** Exposed for BotManager cross-bot awareness */
84
+ get botUserId(): string;
59
85
  start(): Promise<void>;
60
86
  stop(): Promise<void>;
87
+ private checkTriggerCondition;
61
88
  private handleSignal;
62
89
  private processDiscussion;
63
90
  /**
@@ -91,6 +118,11 @@ export declare class Bot {
91
118
  */
92
119
  private cleanupIncompleteExchange;
93
120
  private formatIncomingMessage;
121
+ /**
122
+ * Download image attachments and return Anthropic image content blocks.
123
+ * Falls back silently on errors — the text note still tells the LLM about the file.
124
+ */
125
+ private resolveImageAttachments;
94
126
  private formatOutgoingMessage;
95
127
  /**
96
128
  * Find bare 24-char hex IDs in text that aren't already inside hailerTags
@@ -99,6 +131,8 @@ export declare class Bot {
99
131
  private resolveBareIds;
100
132
  private getBotDisplayName;
101
133
  private buildSystemPrompt;
134
+ private static TOOL_STATUS_LABELS;
135
+ private getToolStatus;
102
136
  private getAnthropicTools;
103
137
  private sendMessage;
104
138
  /**
@@ -113,10 +147,22 @@ export declare class Bot {
113
147
  * Called when process.updated, workspace.updated, or cache.invalidate signals fire.
114
148
  */
115
149
  private refreshWorkspaceData;
150
+ /**
151
+ * Backfill workspaceCache.usersById with entries from network.members
152
+ * that are missing from init.users (which is only a partial user list).
153
+ */
154
+ private backfillWorkspaceCacheMembers;
116
155
  private buildPermissionIndex;
117
156
  private getOrCreateDiscussionState;
118
157
  private getUserContext;
119
158
  private getDefaultTeamId;
159
+ /** Get the current permission context for checkWorkflowAccess calls */
160
+ private getPermissionContext;
161
+ /**
162
+ * Post-filter list-type tool results to remove items the user can't access.
163
+ * Handles list_workflows, list_workflows_minimal, and list_insights.
164
+ */
165
+ private postFilterListResults;
120
166
  /**
121
167
  * Extract workflow ID from tool result for post-execution permission checks.
122
168
  * Returns the workflow ID if found, or undefined if extraction fails.