@hailer/mcp 0.1.15 → 0.1.17

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 (112) hide show
  1. package/.claude/agents/agent-giuseppe-app-builder.md +7 -6
  2. package/.claude/agents/agent-lars-code-inspector.md +26 -14
  3. package/dist/agents/bot-manager.d.ts +48 -0
  4. package/dist/agents/bot-manager.js +254 -0
  5. package/dist/agents/factory.d.ts +150 -0
  6. package/dist/agents/factory.js +650 -0
  7. package/dist/agents/giuseppe/ai.d.ts +83 -0
  8. package/dist/agents/giuseppe/ai.js +466 -0
  9. package/dist/agents/giuseppe/bot.d.ts +110 -0
  10. package/dist/agents/giuseppe/bot.js +780 -0
  11. package/dist/agents/giuseppe/config.d.ts +25 -0
  12. package/dist/agents/giuseppe/config.js +227 -0
  13. package/dist/agents/giuseppe/files.d.ts +52 -0
  14. package/dist/agents/giuseppe/files.js +338 -0
  15. package/dist/agents/giuseppe/git.d.ts +48 -0
  16. package/dist/agents/giuseppe/git.js +298 -0
  17. package/dist/agents/giuseppe/index.d.ts +97 -0
  18. package/dist/agents/giuseppe/index.js +258 -0
  19. package/dist/agents/giuseppe/lsp.d.ts +113 -0
  20. package/dist/agents/giuseppe/lsp.js +485 -0
  21. package/dist/agents/giuseppe/monitor.d.ts +118 -0
  22. package/dist/agents/giuseppe/monitor.js +621 -0
  23. package/dist/agents/giuseppe/prompt.d.ts +5 -0
  24. package/dist/agents/giuseppe/prompt.js +94 -0
  25. package/dist/agents/giuseppe/registries/pending-classification.d.ts +28 -0
  26. package/dist/agents/giuseppe/registries/pending-classification.js +50 -0
  27. package/dist/agents/giuseppe/registries/pending-fix.d.ts +30 -0
  28. package/dist/agents/giuseppe/registries/pending-fix.js +42 -0
  29. package/dist/agents/giuseppe/registries/pending.d.ts +27 -0
  30. package/dist/agents/giuseppe/registries/pending.js +49 -0
  31. package/dist/agents/giuseppe/specialist.d.ts +47 -0
  32. package/dist/agents/giuseppe/specialist.js +237 -0
  33. package/dist/agents/giuseppe/types.d.ts +123 -0
  34. package/dist/agents/giuseppe/types.js +9 -0
  35. package/dist/agents/hailer-expert/index.d.ts +8 -0
  36. package/dist/agents/hailer-expert/index.js +14 -0
  37. package/dist/agents/hal/daemon.d.ts +142 -0
  38. package/dist/agents/hal/daemon.js +1103 -0
  39. package/dist/agents/hal/definitions.d.ts +55 -0
  40. package/dist/agents/hal/definitions.js +263 -0
  41. package/dist/agents/hal/index.d.ts +3 -0
  42. package/dist/agents/hal/index.js +8 -0
  43. package/dist/agents/index.d.ts +18 -0
  44. package/dist/agents/index.js +48 -0
  45. package/dist/agents/shared/base.d.ts +216 -0
  46. package/dist/agents/shared/base.js +846 -0
  47. package/dist/agents/shared/services/agent-registry.d.ts +107 -0
  48. package/dist/agents/shared/services/agent-registry.js +629 -0
  49. package/dist/agents/shared/services/conversation-manager.d.ts +50 -0
  50. package/dist/agents/shared/services/conversation-manager.js +136 -0
  51. package/dist/agents/shared/services/mcp-client.d.ts +56 -0
  52. package/dist/agents/shared/services/mcp-client.js +124 -0
  53. package/dist/agents/shared/services/message-classifier.d.ts +37 -0
  54. package/dist/agents/shared/services/message-classifier.js +187 -0
  55. package/dist/agents/shared/services/message-formatter.d.ts +89 -0
  56. package/dist/agents/shared/services/message-formatter.js +371 -0
  57. package/dist/agents/shared/services/session-logger.d.ts +106 -0
  58. package/dist/agents/shared/services/session-logger.js +446 -0
  59. package/dist/agents/shared/services/tool-executor.d.ts +41 -0
  60. package/dist/agents/shared/services/tool-executor.js +169 -0
  61. package/dist/agents/shared/services/workspace-schema-cache.d.ts +125 -0
  62. package/dist/agents/shared/services/workspace-schema-cache.js +578 -0
  63. package/dist/agents/shared/specialist.d.ts +91 -0
  64. package/dist/agents/shared/specialist.js +399 -0
  65. package/dist/agents/shared/tool-schema-loader.d.ts +62 -0
  66. package/dist/agents/shared/tool-schema-loader.js +232 -0
  67. package/dist/agents/shared/types.d.ts +327 -0
  68. package/dist/agents/shared/types.js +121 -0
  69. package/dist/app.js +21 -4
  70. package/dist/cli.js +0 -0
  71. package/dist/client/agents/orchestrator.d.ts +1 -0
  72. package/dist/client/agents/orchestrator.js +12 -1
  73. package/dist/commands/seed-config.d.ts +9 -0
  74. package/dist/commands/seed-config.js +372 -0
  75. package/dist/config.d.ts +10 -0
  76. package/dist/config.js +61 -1
  77. package/dist/core.d.ts +8 -0
  78. package/dist/core.js +137 -6
  79. package/dist/lib/discussion-lock.d.ts +42 -0
  80. package/dist/lib/discussion-lock.js +110 -0
  81. package/dist/mcp/UserContextCache.js +2 -2
  82. package/dist/mcp/hailer-clients.d.ts +15 -0
  83. package/dist/mcp/hailer-clients.js +100 -6
  84. package/dist/mcp/signal-handler.d.ts +16 -5
  85. package/dist/mcp/signal-handler.js +173 -122
  86. package/dist/mcp/tools/activity.js +9 -1
  87. package/dist/mcp/tools/bot-config.d.ts +184 -9
  88. package/dist/mcp/tools/bot-config.js +2177 -163
  89. package/dist/mcp/tools/giuseppe-tools.d.ts +21 -0
  90. package/dist/mcp/tools/giuseppe-tools.js +525 -0
  91. package/dist/mcp/utils/hailer-api-client.d.ts +42 -1
  92. package/dist/mcp/utils/hailer-api-client.js +128 -2
  93. package/dist/mcp/webhook-handler.d.ts +87 -0
  94. package/dist/mcp/webhook-handler.js +345 -0
  95. package/dist/mcp/workspace-cache.d.ts +5 -0
  96. package/dist/mcp/workspace-cache.js +11 -0
  97. package/dist/mcp-server.js +60 -5
  98. package/dist/modules/bug-reports/giuseppe-agent.d.ts +58 -0
  99. package/dist/modules/bug-reports/giuseppe-agent.js +467 -0
  100. package/dist/modules/bug-reports/giuseppe-ai.d.ts +25 -1
  101. package/dist/modules/bug-reports/giuseppe-ai.js +133 -2
  102. package/dist/modules/bug-reports/giuseppe-bot.d.ts +2 -2
  103. package/dist/modules/bug-reports/giuseppe-bot.js +66 -42
  104. package/dist/modules/bug-reports/giuseppe-daemon.d.ts +80 -0
  105. package/dist/modules/bug-reports/giuseppe-daemon.js +617 -0
  106. package/dist/modules/bug-reports/giuseppe-files.d.ts +12 -0
  107. package/dist/modules/bug-reports/giuseppe-files.js +37 -0
  108. package/dist/modules/bug-reports/giuseppe-lsp.d.ts +84 -13
  109. package/dist/modules/bug-reports/giuseppe-lsp.js +403 -61
  110. package/dist/modules/bug-reports/index.d.ts +1 -0
  111. package/dist/modules/bug-reports/index.js +31 -29
  112. package/package.json +3 -2
@@ -28,12 +28,13 @@ If missing: STOP and request.
28
28
  <rules>
29
29
  1. **READ SKILL FIRST** - Use Read tool on `.claude/skills/hailer-app-builder/SKILL.md` before ANY code. No exceptions.
30
30
  2. **NEVER FABRICATE** - Must call tools.
31
- 3. **Import**: `import useHailer from './hailer/use-hailer'` (local, default!)
32
- 4. **useEffect dep**: `[inside]` ONLY. NEVER include `hailer` or `config` (causes infinite loops)
33
- 5. **Hooks at TOP**: Before any early returns.
34
- 6. **Fields optional**: `fields?: Record<string, { value: unknown }>`
35
- 7. **Theme**: `useColorModeValue('white', 'gray.700')` - no fake tokens.
36
- 8. **JSON ONLY** - Output closing brace, then STOP. Zero prose after JSON.
31
+ 3. **DELEGATE CODE SEARCH TO LARS** - For exploring codebases, finding files, understanding existing code, use Task tool with `agent-lars-code-inspector` (haiku). Lars has LSP + cheaper tokens. You write code, Lars finds code.
32
+ 4. **Import**: `import useHailer from './hailer/use-hailer'` (local, default!)
33
+ 5. **useEffect dep**: `[inside]` ONLY. NEVER include `hailer` or `config` (causes infinite loops)
34
+ 6. **Hooks at TOP**: Before any early returns.
35
+ 7. **Fields optional**: `fields?: Record<string, { value: unknown }>`
36
+ 8. **Theme**: `useColorModeValue('white', 'gray.700')` - no fake tokens.
37
+ 9. **JSON ONLY** - Output closing brace, then STOP. Zero prose after JSON.
37
38
  </rules>
38
39
 
39
40
  <sdk-api>
@@ -2,39 +2,51 @@
2
2
  name: agent-lars-code-inspector
3
3
  description: LSP-powered code intelligence - finds bugs, dead code, unused imports, and navigates codebases.\n\n<example>\nuser: "Find bugs in src/App.tsx"\nassistant: {"status":"success","result":{"dead_code":[{"line":50,"symbol":"setColorMode"}],"unused_imports":[]},"summary":"Found 1 dead code issue"}\n</example>
4
4
  model: haiku
5
- tools: LSP, Read, Glob
5
+ tools: LSP
6
6
  ---
7
7
 
8
8
  <identity>
9
- I am Lars. LSP only. Minimal output. JSON only.
9
+ I am Lars. LSP ONLY. No file reads. No searches. Just LSP.
10
10
  </identity>
11
11
 
12
12
  <handles>
13
- - Find unused variables/imports
14
- - Find dead code
15
- - Type errors
16
- - Duplicate declarations
13
+ - Find unused variables/imports via LSP findReferences
14
+ - Find dead code via LSP documentSymbol + findReferences
15
+ - Type errors via LSP hover/diagnostics
16
+ - Navigate to definitions via LSP goToDefinition
17
17
  </handles>
18
18
 
19
+ <lsp-operations>
20
+ Available LSP operations (USE THESE ONLY):
21
+ - `documentSymbol` - List all symbols in a file
22
+ - `findReferences` - Find all usages of a symbol
23
+ - `goToDefinition` - Jump to where symbol is defined
24
+ - `hover` - Get type info and docs
25
+ - `goToImplementation` - Find implementations
26
+ - `incomingCalls` - What calls this function
27
+ - `outgoingCalls` - What this function calls
28
+ </lsp-operations>
29
+
19
30
  <rules>
20
- 1. **LSP ONLY** - Use LSP operations, never tsc/eslint.
21
- 2. **MINIMAL OUTPUT** - No intermediate results. Final JSON only.
22
- 3. **NO PROSE** - Output JSON, then STOP immediately.
31
+ 1. **LSP ONLY** - ONLY use LSP tool. No Read, no Glob, no Grep, no Bash.
32
+ 2. **REFUSE OTHER TOOLS** - If you don't have LSP, return error. Don't fall back.
33
+ 3. **MINIMAL OUTPUT** - JSON only, no prose.
23
34
  </rules>
24
35
 
25
36
  <workflow>
26
- 1. Use `documentSymbol` to list symbols in file
27
- 2. Use `findReferences` on key symbols to check if used
28
- 3. Check LSP diagnostics in response for errors
29
- 4. Return compact JSON
37
+ 1. `LSP(documentSymbol)` - Get all symbols in file
38
+ 2. `LSP(findReferences)` - Check if each symbol is used
39
+ 3. `LSP(hover)` - Get type info if needed
40
+ 4. Return JSON result
30
41
  </workflow>
31
42
 
32
43
  <protocol>
33
- Output JSON only. No explanations. No tool output dumps.
44
+ Output JSON only:
34
45
  {
35
46
  "status": "success|error",
36
47
  "result": {
37
48
  "dead_code": [{"line":0,"symbol":""}],
49
+ "unused_imports": [{"line":0,"module":""}],
38
50
  "type_errors": [{"line":0,"msg":""}]
39
51
  },
40
52
  "summary": "max 30 chars"
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Multi-Bot Client Manager
3
+ * Manages multiple bot clients, each with their own Hailer connection
4
+ */
5
+ import { HailerClient } from "../mcp/hailer-clients";
6
+ import { SignalHandler } from "../mcp/signal-handler";
7
+ import { BotClientConfig } from "./shared/types";
8
+ import { WorkspaceCache } from "../mcp/workspace-cache";
9
+ export interface BotClient {
10
+ userId: string;
11
+ firstName: string;
12
+ lastName: string;
13
+ config: BotClientConfig;
14
+ client: HailerClient;
15
+ signalHandler: SignalHandler;
16
+ workspaceCache?: WorkspaceCache;
17
+ }
18
+ export declare class MultiBotManager {
19
+ private botConfigs;
20
+ private botClients;
21
+ constructor(botConfigs: BotClientConfig[]);
22
+ initializeAllHailerClientsFromConfig(): Promise<void>;
23
+ getBotClient(userId: string): BotClient | undefined;
24
+ getAllBotClients(): BotClient[];
25
+ getBotIds(): string[];
26
+ private userNameCache;
27
+ private static USER_CACHE_TTL;
28
+ /**
29
+ * Look up a user's full name by their user ID
30
+ * Uses workspace cache first, then falls back to socket API call
31
+ */
32
+ getUserName(userId: string): string | undefined;
33
+ /**
34
+ * Initialize a single bot client from credentials
35
+ * Used for dynamic bot creation from AI Hub
36
+ */
37
+ initializeBotClient(email: string, password: string, apiKey: string): Promise<string>;
38
+ /**
39
+ * Remove and disconnect a bot client
40
+ */
41
+ removeBotClient(userId: string): Promise<void>;
42
+ /**
43
+ * Get bot client by bot ID (hal, giuseppe, hailerExpert, vastuullisuus)
44
+ */
45
+ getBotClientByBotId(botId: string): BotClient | undefined;
46
+ shutdown(): Promise<void>;
47
+ }
48
+ //# sourceMappingURL=bot-manager.d.ts.map
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ /**
3
+ * Multi-Bot Client Manager
4
+ * Manages multiple bot clients, each with their own Hailer connection
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.MultiBotManager = void 0;
8
+ const hailer_clients_1 = require("../mcp/hailer-clients");
9
+ const signal_handler_1 = require("../mcp/signal-handler");
10
+ const workspace_cache_1 = require("../mcp/workspace-cache");
11
+ const config_1 = require("../config");
12
+ const bot_config_1 = require("../mcp/tools/bot-config");
13
+ const logger_1 = require("../lib/logger");
14
+ const logger = (0, logger_1.createLogger)({ component: 'bot-manager' });
15
+ class MultiBotManager {
16
+ botConfigs;
17
+ botClients = new Map();
18
+ constructor(botConfigs) {
19
+ this.botConfigs = botConfigs;
20
+ }
21
+ // for client we may put initializeAllConfigClients method to do it at once
22
+ // in case we only use MCP Server:
23
+ // for MCP Server it may just get a specific client and this class initialize it specifically and even kill it later if not needed
24
+ async initializeAllHailerClientsFromConfig() {
25
+ logger.info('Initializing bot clients', { count: this.botConfigs.length });
26
+ const appConfig = (0, config_1.createApplicationConfig)();
27
+ for (const config of this.botConfigs) {
28
+ try {
29
+ // Use shared connection pool via API key lookup
30
+ const client = await (0, hailer_clients_1.createHailerClientByApiKey)(config.mcpServerApiKey);
31
+ // Get user ID automatically from authenticated session
32
+ const userId = await (0, hailer_clients_1.getCurrentUserId)(client);
33
+ // Fetch init data to get workspace cache with user names and teams
34
+ let workspaceCache;
35
+ try {
36
+ const init = await client.socket.request('v2.core.init', [['users', 'network', 'networks', 'teams']]);
37
+ workspaceCache = (0, workspace_cache_1.createWorkspaceCache)(init, appConfig.mcpConfig);
38
+ const teamCount = Object.keys(init.teams || {}).length;
39
+ logger.info('Loaded workspace cache', { userCount: workspaceCache.users.length, teamCount });
40
+ }
41
+ catch (initError) {
42
+ logger.warn('Could not load workspace cache', { email: (0, config_1.maskEmail)(config.email), error: String(initError) });
43
+ }
44
+ const signalHandler = signal_handler_1.SignalHandler.getOrCreate(client, workspaceCache);
45
+ // Look up user's name from workspace cache
46
+ let firstName = 'Bot';
47
+ let lastName = '';
48
+ if (workspaceCache) {
49
+ const userInfo = workspaceCache.usersById[userId];
50
+ if (userInfo) {
51
+ firstName = userInfo.firstname || 'Bot';
52
+ lastName = userInfo.lastname || '';
53
+ }
54
+ }
55
+ // Check if we have a display name from Agent Directory and update Hailer profile
56
+ const localCreds = (0, bot_config_1.getLocalBotCredentials)(userId);
57
+ if (localCreds?.displayName) {
58
+ const currentFullName = `${firstName} ${lastName}`.trim();
59
+ if (currentFullName !== localCreds.displayName) {
60
+ try {
61
+ // Parse display name into first/last
62
+ const parts = localCreds.displayName.trim().split(/\s+/);
63
+ const newFirstName = parts[0] || localCreds.displayName;
64
+ const newLastName = parts.slice(1).join(' ') || '';
65
+ // Update Hailer profile via API
66
+ await client.socket.request('user.set_user_info', ['firstname', [newFirstName]]);
67
+ if (newLastName) {
68
+ await client.socket.request('user.set_user_info', ['lastname', [newLastName]]);
69
+ }
70
+ firstName = newFirstName;
71
+ lastName = newLastName;
72
+ logger.info('Updated bot display name', { from: currentFullName, to: localCreds.displayName });
73
+ }
74
+ catch (nameError) {
75
+ logger.warn('Could not update bot display name', { error: String(nameError) });
76
+ }
77
+ }
78
+ }
79
+ const botClient = {
80
+ userId,
81
+ firstName,
82
+ lastName,
83
+ config,
84
+ client,
85
+ signalHandler,
86
+ workspaceCache,
87
+ };
88
+ this.botClients.set(userId, botClient);
89
+ logger.info('Bot client initialized', {
90
+ email: (0, config_1.maskEmail)(config.email),
91
+ userId,
92
+ apiKey: (0, config_1.maskSensitiveData)(config.mcpServerApiKey),
93
+ });
94
+ }
95
+ catch (error) {
96
+ logger.error('Failed to initialize bot client', error, {
97
+ email: (0, config_1.maskEmail)(config.email),
98
+ });
99
+ // Continue with other bots even if one fails
100
+ }
101
+ }
102
+ logger.info('Bot clients initialization complete', { count: this.botClients.size });
103
+ }
104
+ getBotClient(userId) {
105
+ const botClient = this.botClients.get(userId);
106
+ if (!botClient) {
107
+ logger.warn('No bot client found', { userId });
108
+ }
109
+ return botClient;
110
+ }
111
+ getAllBotClients() {
112
+ return Array.from(this.botClients.values());
113
+ }
114
+ getBotIds() {
115
+ return Array.from(this.botClients.keys());
116
+ }
117
+ // Simple in-memory cache for user lookups (refreshed on demand)
118
+ userNameCache = new Map();
119
+ static USER_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
120
+ /**
121
+ * Look up a user's full name by their user ID
122
+ * Uses workspace cache first, then falls back to socket API call
123
+ */
124
+ getUserName(userId) {
125
+ // Check in-memory cache first (for API-fetched users)
126
+ const cached = this.userNameCache.get(userId);
127
+ if (cached && Date.now() - cached.fetchedAt < MultiBotManager.USER_CACHE_TTL) {
128
+ return cached.name;
129
+ }
130
+ // Try each bot client's workspace cache until we find the user
131
+ for (const botClient of this.botClients.values()) {
132
+ if (botClient.workspaceCache) {
133
+ const user = (0, workspace_cache_1.getUserById)(botClient.workspaceCache, userId);
134
+ if (user) {
135
+ const fullName = `${user.firstname || ''} ${user.lastname || ''}`.trim();
136
+ return fullName || user.fullName || undefined;
137
+ }
138
+ }
139
+ }
140
+ return undefined;
141
+ }
142
+ /**
143
+ * Initialize a single bot client from credentials
144
+ * Used for dynamic bot creation from AI Hub
145
+ */
146
+ async initializeBotClient(email, password, apiKey) {
147
+ const appConfig = (0, config_1.createApplicationConfig)();
148
+ // Check if bot with this email already exists
149
+ const existingBot = Array.from(this.botClients.values())
150
+ .find(bc => bc.config.email === email);
151
+ if (existingBot) {
152
+ logger.info('Bot client already exists', { email: (0, config_1.maskEmail)(email) });
153
+ return existingBot.userId;
154
+ }
155
+ const config = {
156
+ email,
157
+ password,
158
+ apiBaseUrl: 'https://api.hailer.com',
159
+ mcpServerApiKey: apiKey,
160
+ };
161
+ try {
162
+ const client = await (0, hailer_clients_1.createHailerClientByApiKey)(apiKey);
163
+ const userId = await (0, hailer_clients_1.getCurrentUserId)(client);
164
+ let workspaceCache;
165
+ let workspaceId;
166
+ try {
167
+ const init = await client.socket.request('v2.core.init', [['users', 'network', 'networks', 'teams']]);
168
+ workspaceCache = (0, workspace_cache_1.createWorkspaceCache)(init, appConfig.mcpConfig);
169
+ workspaceId = init?.network?._id;
170
+ }
171
+ catch (initError) {
172
+ logger.warn('Could not load workspace cache', { email: (0, config_1.maskEmail)(email), error: String(initError) });
173
+ }
174
+ const signalHandler = signal_handler_1.SignalHandler.getOrCreate(client, workspaceCache);
175
+ // Initialize workspace schema for this bot's workspace (discover Agent Directory)
176
+ if (workspaceId) {
177
+ try {
178
+ const hailerApiClient = { getClient: () => client, fetchInit: (modules) => client.socket.request('v2.core.init', [modules]), listWorkflows: async () => {
179
+ const init = await client.socket.request('v2.core.init', [['processes']]);
180
+ return Object.values(init?.processes || {});
181
+ } };
182
+ await (0, bot_config_1.initWorkspaceSchema)(hailerApiClient, workspaceId);
183
+ logger.info('Workspace schema initialized', { email: (0, config_1.maskEmail)(email), workspaceId });
184
+ }
185
+ catch (schemaError) {
186
+ logger.warn('Could not initialize workspace schema', { email: (0, config_1.maskEmail)(email), error: String(schemaError) });
187
+ }
188
+ }
189
+ let firstName = 'Bot';
190
+ let lastName = '';
191
+ if (workspaceCache) {
192
+ const userInfo = workspaceCache.usersById[userId];
193
+ if (userInfo) {
194
+ firstName = userInfo.firstname || 'Bot';
195
+ lastName = userInfo.lastname || '';
196
+ }
197
+ }
198
+ const botClient = {
199
+ userId,
200
+ firstName,
201
+ lastName,
202
+ config,
203
+ client,
204
+ signalHandler,
205
+ workspaceCache,
206
+ };
207
+ this.botClients.set(userId, botClient);
208
+ logger.info('Bot client initialized', { email: (0, config_1.maskEmail)(email), userId });
209
+ return userId;
210
+ }
211
+ catch (error) {
212
+ logger.error('Failed to initialize bot client', error, { email: (0, config_1.maskEmail)(email) });
213
+ throw error;
214
+ }
215
+ }
216
+ /**
217
+ * Remove and disconnect a bot client
218
+ */
219
+ async removeBotClient(userId) {
220
+ const botClient = this.botClients.get(userId);
221
+ if (!botClient) {
222
+ logger.warn('Bot client not found for removal', { userId });
223
+ return;
224
+ }
225
+ (0, hailer_clients_1.disconnectHailerClientByApiKey)(botClient.config.mcpServerApiKey);
226
+ this.botClients.delete(userId);
227
+ logger.info('Bot client removed', { userId });
228
+ }
229
+ /**
230
+ * Get bot client by bot ID (hal, giuseppe, hailerExpert, vastuullisuus)
231
+ */
232
+ getBotClientByBotId(botId) {
233
+ // Match by email containing the bot ID
234
+ return Array.from(this.botClients.values())
235
+ .find(bc => bc.config.email.toLowerCase().includes(botId.toLowerCase()));
236
+ }
237
+ async shutdown() {
238
+ logger.info('Shutting down all bot clients', { count: this.botClients.size });
239
+ for (const [userId, botClient] of this.botClients) {
240
+ try {
241
+ // Properly disconnect from shared connection pool
242
+ (0, hailer_clients_1.disconnectHailerClientByApiKey)(botClient.config.mcpServerApiKey);
243
+ logger.info('Bot client shut down', { userId, apiKey: (0, config_1.maskSensitiveData)(botClient.config.mcpServerApiKey) });
244
+ }
245
+ catch (error) {
246
+ logger.error('Error shutting down bot client', error, { userId });
247
+ }
248
+ }
249
+ this.botClients.clear();
250
+ logger.info('All bot clients shut down');
251
+ }
252
+ }
253
+ exports.MultiBotManager = MultiBotManager;
254
+ //# sourceMappingURL=bot-manager.js.map
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Daemon Factory
3
+ *
4
+ * Creates and manages agent daemons in orchestrator mode:
5
+ * - One orchestrator (HAL) handles general conversation
6
+ * - Specialist daemons handle complex domain-specific tasks
7
+ */
8
+ import { ChatAgentDaemon } from "./shared/base";
9
+ import { OrchestratorDaemon } from "./hal/daemon";
10
+ import { SpecialistDaemon } from "./shared/specialist";
11
+ import { MultiBotManager } from "./bot-manager";
12
+ import { ConversationState } from "./shared/types";
13
+ export interface DaemonManagerOptions {
14
+ mcpServerUrl: string;
15
+ anthropicApiKey: string;
16
+ model?: string;
17
+ /**
18
+ * Enable orchestrator mode (default: true)
19
+ * Set to false only for legacy compatibility
20
+ */
21
+ orchestratorMode?: boolean;
22
+ /**
23
+ * Email of the bot to use as orchestrator (defaults to first bot)
24
+ */
25
+ orchestratorEmail?: string;
26
+ /**
27
+ * Map of specialist key to bot email (e.g., { hailerExpert: "expert@bot.com" })
28
+ */
29
+ specialistEmails?: Record<string, string>;
30
+ /**
31
+ * Map of userId to botType (from workspace config)
32
+ */
33
+ botTypeMap?: Map<string, string>;
34
+ /**
35
+ * Map of userId to displayName (from Agent Directory activity name)
36
+ */
37
+ displayNameMap?: Map<string, string>;
38
+ /**
39
+ * Specific workspace ID to use (for webhook-triggered restarts)
40
+ */
41
+ workspaceId?: string;
42
+ }
43
+ /**
44
+ * Manages orchestrator + specialist daemons
45
+ */
46
+ export declare class DaemonManager {
47
+ private daemons;
48
+ private orchestrator;
49
+ private specialists;
50
+ private botManager;
51
+ private options;
52
+ private botTypeMap;
53
+ private displayNameMap;
54
+ constructor(botManager: MultiBotManager, options: DaemonManagerOptions);
55
+ /**
56
+ * Get botType for a userId from the workspace config
57
+ */
58
+ private getBotType;
59
+ /**
60
+ * Get displayName for a userId from the workspace config (Agent Directory activity name)
61
+ * Falls back to Hailer profile name if not set
62
+ */
63
+ private getDisplayName;
64
+ /**
65
+ * Start all daemons (orchestrator + specialists)
66
+ */
67
+ startAll(): Promise<void>;
68
+ /**
69
+ * Orchestrator mode: one orchestrator (HAL) + specialist daemons
70
+ */
71
+ private startOrchestratorMode;
72
+ /**
73
+ * Stop all daemons
74
+ * Flushes session logs for each daemon before stopping
75
+ */
76
+ stopAll(): Promise<void>;
77
+ /**
78
+ * Get daemon for a specific bot
79
+ */
80
+ getDaemon(botId: string): ChatAgentDaemon | undefined;
81
+ /**
82
+ * Get status of all daemons
83
+ */
84
+ getStatus(): Array<{
85
+ botId: string;
86
+ state: ConversationState;
87
+ }>;
88
+ /**
89
+ * Log current status to console (for monitoring)
90
+ */
91
+ logStatus(): void;
92
+ /**
93
+ * Get orchestrator instance (only in orchestrator mode)
94
+ */
95
+ getOrchestrator(): OrchestratorDaemon | null;
96
+ /**
97
+ * Get specialist by key (only in orchestrator mode)
98
+ */
99
+ getSpecialist(key: string): SpecialistDaemon | undefined;
100
+ /**
101
+ * Check if running in orchestrator mode
102
+ */
103
+ isOrchestratorMode(): boolean;
104
+ /**
105
+ * Trigger HAL to respond in a discussion with context
106
+ */
107
+ triggerHalResponse(discussionId: string, activityId: string, context: string): Promise<void>;
108
+ /**
109
+ * Hot-reload: Start a specialist daemon dynamically
110
+ * Preserves orchestrator conversation context
111
+ */
112
+ startSpecialist(email: string, password: string, botType: string, userId?: string): Promise<boolean>;
113
+ /**
114
+ * Hot-reload: Stop a specialist daemon dynamically
115
+ * Preserves orchestrator conversation context
116
+ */
117
+ stopSpecialist(botType: string): Promise<boolean>;
118
+ /**
119
+ * Hot-reload a specialist (stop if running, start if enabled)
120
+ */
121
+ hotReloadSpecialist(email: string, password: string, botType: string, enabled: boolean, userId?: string): Promise<boolean>;
122
+ /**
123
+ * Start periodic status logging
124
+ */
125
+ startStatusLogging(intervalMs?: number): NodeJS.Timeout;
126
+ }
127
+ export interface CreateDaemonManagerOptions {
128
+ /** Enable orchestrator mode */
129
+ orchestratorMode?: boolean;
130
+ /** Email of orchestrator bot (defaults to first) */
131
+ orchestratorEmail?: string;
132
+ /** Map specialist keys to bot emails */
133
+ specialistEmails?: Record<string, string>;
134
+ /** Specific workspace ID to use (for webhook-triggered restarts) */
135
+ workspaceId?: string;
136
+ }
137
+ /**
138
+ * Create and start the daemon manager
139
+ * This is the main entry point for daemon mode
140
+ *
141
+ * @param options - Optional settings for orchestrator mode
142
+ */
143
+ export declare function createDaemonManager(options?: CreateDaemonManagerOptions): Promise<DaemonManager | null>;
144
+ /**
145
+ * Quick start function for testing
146
+ *
147
+ * @param orchestratorMode - Enable orchestrator mode (default: true)
148
+ */
149
+ export declare function startDaemonMode(orchestratorMode?: boolean): Promise<void>;
150
+ //# sourceMappingURL=factory.d.ts.map