@hailer/mcp 0.1.17 → 0.2.1
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.
- package/dist/app.js +24 -20
- package/dist/core.d.ts +33 -9
- package/dist/core.js +279 -147
- package/dist/mcp/UserContextCache.js +18 -0
- package/dist/mcp/hailer-clients.d.ts +9 -1
- package/dist/mcp/hailer-clients.js +13 -3
- package/dist/mcp/signal-handler.js +1 -1
- package/dist/mcp/tool-registry.d.ts +3 -1
- package/dist/mcp/tool-registry.js +4 -1
- package/dist/mcp/tools/activity.js +43 -34
- package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
- package/dist/mcp/tools/bot-config/constants.js +94 -0
- package/dist/mcp/tools/{bot-config.d.ts → bot-config/core.d.ts} +6 -6
- package/dist/mcp/tools/{bot-config.js → bot-config/core.js} +15 -15
- package/dist/mcp/tools/bot-config/index.d.ts +10 -0
- package/dist/mcp/tools/bot-config/index.js +59 -0
- package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
- package/dist/mcp/tools/bot-config/tools.js +15 -0
- package/dist/mcp/tools/bot-config/types.d.ts +50 -0
- package/dist/mcp/tools/bot-config/types.js +6 -0
- package/dist/mcp/tools/bug-fixer-tools.d.ts +21 -0
- package/dist/mcp/tools/{giuseppe-tools.js → bug-fixer-tools.js} +61 -61
- package/dist/mcp/tools/user.js +10 -29
- package/dist/mcp/tools/workflow.js +36 -2
- package/dist/mcp/utils/data-transformers.d.ts +0 -8
- package/dist/mcp/utils/data-transformers.js +0 -28
- package/dist/mcp/utils/index.d.ts +4 -1
- package/dist/mcp/utils/index.js +17 -3
- package/dist/mcp/utils/pagination.d.ts +40 -0
- package/dist/mcp/utils/pagination.js +55 -0
- package/dist/mcp/utils/response-builder.d.ts +53 -0
- package/dist/mcp/utils/response-builder.js +110 -0
- package/dist/mcp/utils/tool-helpers.d.ts +0 -8
- package/dist/mcp/utils/tool-helpers.js +0 -24
- package/dist/mcp/utils/types.d.ts +1 -33
- package/dist/mcp-server.d.ts +2 -2
- package/dist/mcp-server.js +161 -139
- package/package.json +1 -1
- package/REFACTOR_STATUS.md +0 -127
- package/dist/agents/bot-manager.d.ts +0 -48
- package/dist/agents/bot-manager.js +0 -254
- package/dist/agents/factory.d.ts +0 -150
- package/dist/agents/factory.js +0 -650
- package/dist/agents/giuseppe/ai.d.ts +0 -83
- package/dist/agents/giuseppe/ai.js +0 -466
- package/dist/agents/giuseppe/bot.d.ts +0 -110
- package/dist/agents/giuseppe/bot.js +0 -780
- package/dist/agents/giuseppe/config.d.ts +0 -25
- package/dist/agents/giuseppe/config.js +0 -227
- package/dist/agents/giuseppe/files.d.ts +0 -52
- package/dist/agents/giuseppe/files.js +0 -338
- package/dist/agents/giuseppe/git.d.ts +0 -48
- package/dist/agents/giuseppe/git.js +0 -298
- package/dist/agents/giuseppe/index.d.ts +0 -97
- package/dist/agents/giuseppe/index.js +0 -258
- package/dist/agents/giuseppe/lsp.d.ts +0 -113
- package/dist/agents/giuseppe/lsp.js +0 -485
- package/dist/agents/giuseppe/monitor.d.ts +0 -118
- package/dist/agents/giuseppe/monitor.js +0 -621
- package/dist/agents/giuseppe/prompt.d.ts +0 -5
- package/dist/agents/giuseppe/prompt.js +0 -94
- package/dist/agents/giuseppe/registries/pending-classification.d.ts +0 -28
- package/dist/agents/giuseppe/registries/pending-classification.js +0 -50
- package/dist/agents/giuseppe/registries/pending-fix.d.ts +0 -30
- package/dist/agents/giuseppe/registries/pending-fix.js +0 -42
- package/dist/agents/giuseppe/registries/pending.d.ts +0 -27
- package/dist/agents/giuseppe/registries/pending.js +0 -49
- package/dist/agents/giuseppe/specialist.d.ts +0 -47
- package/dist/agents/giuseppe/specialist.js +0 -237
- package/dist/agents/giuseppe/types.d.ts +0 -123
- package/dist/agents/giuseppe/types.js +0 -9
- package/dist/agents/hailer-expert/index.d.ts +0 -8
- package/dist/agents/hailer-expert/index.js +0 -14
- package/dist/agents/hal/daemon.d.ts +0 -142
- package/dist/agents/hal/daemon.js +0 -1103
- package/dist/agents/hal/definitions.d.ts +0 -55
- package/dist/agents/hal/definitions.js +0 -263
- package/dist/agents/hal/index.d.ts +0 -3
- package/dist/agents/hal/index.js +0 -8
- package/dist/agents/index.d.ts +0 -18
- package/dist/agents/index.js +0 -48
- package/dist/agents/shared/base.d.ts +0 -216
- package/dist/agents/shared/base.js +0 -846
- package/dist/agents/shared/services/agent-registry.d.ts +0 -107
- package/dist/agents/shared/services/agent-registry.js +0 -629
- package/dist/agents/shared/services/conversation-manager.d.ts +0 -50
- package/dist/agents/shared/services/conversation-manager.js +0 -136
- package/dist/agents/shared/services/mcp-client.d.ts +0 -56
- package/dist/agents/shared/services/mcp-client.js +0 -124
- package/dist/agents/shared/services/message-classifier.d.ts +0 -37
- package/dist/agents/shared/services/message-classifier.js +0 -187
- package/dist/agents/shared/services/message-formatter.d.ts +0 -89
- package/dist/agents/shared/services/message-formatter.js +0 -371
- package/dist/agents/shared/services/session-logger.d.ts +0 -106
- package/dist/agents/shared/services/session-logger.js +0 -446
- package/dist/agents/shared/services/tool-executor.d.ts +0 -41
- package/dist/agents/shared/services/tool-executor.js +0 -169
- package/dist/agents/shared/services/workspace-schema-cache.d.ts +0 -125
- package/dist/agents/shared/services/workspace-schema-cache.js +0 -578
- package/dist/agents/shared/specialist.d.ts +0 -91
- package/dist/agents/shared/specialist.js +0 -399
- package/dist/agents/shared/tool-schema-loader.d.ts +0 -62
- package/dist/agents/shared/tool-schema-loader.js +0 -232
- package/dist/agents/shared/types.d.ts +0 -327
- package/dist/agents/shared/types.js +0 -121
- package/dist/client/agents/base.d.ts +0 -207
- package/dist/client/agents/base.js +0 -744
- package/dist/client/agents/definitions.d.ts +0 -53
- package/dist/client/agents/definitions.js +0 -263
- package/dist/client/agents/orchestrator.d.ts +0 -141
- package/dist/client/agents/orchestrator.js +0 -1062
- package/dist/client/agents/specialist.d.ts +0 -86
- package/dist/client/agents/specialist.js +0 -340
- package/dist/client/bot-entrypoint.d.ts +0 -7
- package/dist/client/bot-entrypoint.js +0 -103
- package/dist/client/bot-manager.d.ts +0 -44
- package/dist/client/bot-manager.js +0 -173
- package/dist/client/bot-runner.d.ts +0 -35
- package/dist/client/bot-runner.js +0 -188
- package/dist/client/chat-agent-daemon.d.ts +0 -464
- package/dist/client/chat-agent-daemon.js +0 -1774
- package/dist/client/daemon-factory.d.ts +0 -106
- package/dist/client/daemon-factory.js +0 -301
- package/dist/client/factory.d.ts +0 -111
- package/dist/client/factory.js +0 -314
- package/dist/client/index.d.ts +0 -17
- package/dist/client/index.js +0 -38
- package/dist/client/multi-bot-manager.d.ts +0 -42
- package/dist/client/multi-bot-manager.js +0 -161
- package/dist/client/orchestrator-daemon.d.ts +0 -87
- package/dist/client/orchestrator-daemon.js +0 -444
- package/dist/client/server.d.ts +0 -8
- package/dist/client/server.js +0 -251
- package/dist/client/services/agent-registry.d.ts +0 -108
- package/dist/client/services/agent-registry.js +0 -630
- package/dist/client/services/conversation-manager.d.ts +0 -50
- package/dist/client/services/conversation-manager.js +0 -136
- package/dist/client/services/mcp-client.d.ts +0 -48
- package/dist/client/services/mcp-client.js +0 -105
- package/dist/client/services/message-classifier.d.ts +0 -37
- package/dist/client/services/message-classifier.js +0 -187
- package/dist/client/services/message-formatter.d.ts +0 -84
- package/dist/client/services/message-formatter.js +0 -353
- package/dist/client/services/session-logger.d.ts +0 -106
- package/dist/client/services/session-logger.js +0 -446
- package/dist/client/services/tool-executor.d.ts +0 -41
- package/dist/client/services/tool-executor.js +0 -169
- package/dist/client/services/workspace-schema-cache.d.ts +0 -149
- package/dist/client/services/workspace-schema-cache.js +0 -732
- package/dist/client/specialist-daemon.d.ts +0 -77
- package/dist/client/specialist-daemon.js +0 -197
- package/dist/client/specialists.d.ts +0 -53
- package/dist/client/specialists.js +0 -178
- package/dist/client/tool-schema-loader.d.ts +0 -62
- package/dist/client/tool-schema-loader.js +0 -232
- package/dist/client/types.d.ts +0 -327
- package/dist/client/types.js +0 -121
- package/dist/commands/seed-config.d.ts +0 -9
- package/dist/commands/seed-config.js +0 -372
- package/dist/lib/context-manager.d.ts +0 -111
- package/dist/lib/context-manager.js +0 -431
- package/dist/lib/prompt-length-manager.d.ts +0 -81
- package/dist/lib/prompt-length-manager.js +0 -457
- package/dist/mcp/tools/giuseppe-tools.d.ts +0 -21
- package/dist/modules/bug-reports/bug-config.d.ts +0 -25
- package/dist/modules/bug-reports/bug-config.js +0 -187
- package/dist/modules/bug-reports/bug-monitor.d.ts +0 -108
- package/dist/modules/bug-reports/bug-monitor.js +0 -510
- package/dist/modules/bug-reports/giuseppe-agent.d.ts +0 -58
- package/dist/modules/bug-reports/giuseppe-agent.js +0 -467
- package/dist/modules/bug-reports/giuseppe-ai.d.ts +0 -83
- package/dist/modules/bug-reports/giuseppe-ai.js +0 -466
- package/dist/modules/bug-reports/giuseppe-bot.d.ts +0 -110
- package/dist/modules/bug-reports/giuseppe-bot.js +0 -804
- package/dist/modules/bug-reports/giuseppe-daemon.d.ts +0 -80
- package/dist/modules/bug-reports/giuseppe-daemon.js +0 -617
- package/dist/modules/bug-reports/giuseppe-files.d.ts +0 -64
- package/dist/modules/bug-reports/giuseppe-files.js +0 -375
- package/dist/modules/bug-reports/giuseppe-git.d.ts +0 -48
- package/dist/modules/bug-reports/giuseppe-git.js +0 -298
- package/dist/modules/bug-reports/giuseppe-lsp.d.ts +0 -113
- package/dist/modules/bug-reports/giuseppe-lsp.js +0 -485
- package/dist/modules/bug-reports/giuseppe-prompt.d.ts +0 -5
- package/dist/modules/bug-reports/giuseppe-prompt.js +0 -94
- package/dist/modules/bug-reports/index.d.ts +0 -77
- package/dist/modules/bug-reports/index.js +0 -215
- package/dist/modules/bug-reports/pending-classification-registry.d.ts +0 -28
- package/dist/modules/bug-reports/pending-classification-registry.js +0 -50
- package/dist/modules/bug-reports/pending-fix-registry.d.ts +0 -30
- package/dist/modules/bug-reports/pending-fix-registry.js +0 -42
- package/dist/modules/bug-reports/pending-registry.d.ts +0 -27
- package/dist/modules/bug-reports/pending-registry.js +0 -49
- package/dist/modules/bug-reports/types.d.ts +0 -123
- package/dist/modules/bug-reports/types.js +0 -9
- package/dist/routes/agents.d.ts +0 -44
- package/dist/routes/agents.js +0 -311
- package/dist/services/agent-credential-store.d.ts +0 -73
- package/dist/services/agent-credential-store.js +0 -212
- package/dist/services/bug-monitor.d.ts +0 -23
- package/dist/services/bug-monitor.js +0 -275
package/dist/core.js
CHANGED
|
@@ -8,23 +8,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
8
8
|
exports.Core = void 0;
|
|
9
9
|
const logger_1 = require("./lib/logger");
|
|
10
10
|
const config_1 = require("./config");
|
|
11
|
-
const factory_1 = require("./agents/factory");
|
|
12
11
|
const mcp_server_1 = require("./mcp-server");
|
|
13
12
|
const tool_registry_1 = require("./mcp/tool-registry");
|
|
14
|
-
const giuseppe_1 = require("./agents/giuseppe");
|
|
15
13
|
const UserContextCache_1 = require("./mcp/UserContextCache");
|
|
16
|
-
const bot_config_1 = require("./mcp/tools/bot-config");
|
|
17
14
|
const webhook_handler_1 = require("./mcp/webhook-handler");
|
|
18
15
|
class Core {
|
|
19
16
|
logger;
|
|
20
17
|
appConfig;
|
|
21
18
|
toolRegistry;
|
|
22
19
|
mcpServer;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
daemonInitInProgress =
|
|
27
|
-
initialDaemonReady =
|
|
20
|
+
daemonManagers = new Map(); // workspace ID -> DaemonManager
|
|
21
|
+
statusLogIntervals = new Map(); // workspace ID -> interval
|
|
22
|
+
bugReportsModules = new Map(); // workspace ID -> BugReportsModule
|
|
23
|
+
daemonInitInProgress = new Set(); // workspace IDs currently initializing
|
|
24
|
+
initialDaemonReady = new Set(); // workspace IDs that have completed first init
|
|
28
25
|
constructor() {
|
|
29
26
|
// Initialize logger first
|
|
30
27
|
this.logger = (0, logger_1.createLogger)({
|
|
@@ -65,14 +62,15 @@ class Core {
|
|
|
65
62
|
await this.startMCPServer();
|
|
66
63
|
}
|
|
67
64
|
}
|
|
68
|
-
//
|
|
69
|
-
await this.initBotConfig();
|
|
70
|
-
// Start client/daemon AFTER MCP server is ready
|
|
65
|
+
// Start client/daemon services ONLY if MCP_CLIENT_ENABLED=true
|
|
71
66
|
if (this.appConfig.server.enableClient) {
|
|
67
|
+
// Initialize bot config persistence (reads config from Hailer Agent Directory)
|
|
68
|
+
await this.initBotConfig();
|
|
69
|
+
// Start daemons for all workspaces with orchestrator
|
|
72
70
|
await this.startMCPClient();
|
|
71
|
+
// Start Bug Monitor service (reads config from Hailer)
|
|
72
|
+
await this.startBugMonitor();
|
|
73
73
|
}
|
|
74
|
-
// Start Bug Monitor service (reads config from Hailer)
|
|
75
|
-
await this.startBugMonitor();
|
|
76
74
|
this.setupGracefulShutdown();
|
|
77
75
|
this.logger.info('All configured services started successfully');
|
|
78
76
|
}
|
|
@@ -93,61 +91,117 @@ class Core {
|
|
|
93
91
|
await this.mcpServer.start();
|
|
94
92
|
this.logger.info('MCP Server service started');
|
|
95
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Start MCP Client (daemons) for all workspaces with enabled orchestrators
|
|
96
|
+
*/
|
|
96
97
|
async startMCPClient() {
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
this.logger.info('Starting Chat Agent Daemons for all enabled workspaces');
|
|
99
|
+
// Load all workspace configs
|
|
100
|
+
const { loadBotConfigs } = require('./bot-config');
|
|
101
|
+
const botContexts = loadBotConfigs();
|
|
102
|
+
// Find all workspaces with enabled orchestrators
|
|
103
|
+
const workspacesWithOrchestrator = botContexts.filter((ctx) => ctx.getOrchestratorCredentials());
|
|
104
|
+
if (workspacesWithOrchestrator.length === 0) {
|
|
105
|
+
this.logger.info('No workspaces with orchestrator found - skipping daemon start');
|
|
99
106
|
return;
|
|
100
107
|
}
|
|
101
|
-
this.logger.info('
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
108
|
+
this.logger.info('Found workspaces with orchestrator', {
|
|
109
|
+
count: workspacesWithOrchestrator.length,
|
|
110
|
+
workspaces: workspacesWithOrchestrator.map((ctx) => ({
|
|
111
|
+
id: ctx.workspaceId,
|
|
112
|
+
name: ctx.workspaceName
|
|
113
|
+
}))
|
|
114
|
+
});
|
|
115
|
+
// Start daemon for each workspace
|
|
116
|
+
const startPromises = workspacesWithOrchestrator.map(async (ctx) => {
|
|
117
|
+
const workspaceId = ctx.workspaceId;
|
|
118
|
+
if (this.daemonInitInProgress.has(workspaceId)) {
|
|
119
|
+
this.logger.debug('Daemon init already in progress, skipping', { workspaceId });
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
this.daemonInitInProgress.add(workspaceId);
|
|
123
|
+
try {
|
|
124
|
+
await this.initializeDaemonMode(workspaceId);
|
|
125
|
+
this.initialDaemonReady.add(workspaceId);
|
|
126
|
+
this.logger.info('Chat Agent Daemon started', { workspaceId });
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
this.logger.warn('Daemon mode failed to start for workspace - continuing with other workspaces', {
|
|
130
|
+
workspaceId,
|
|
131
|
+
error: error instanceof Error ? error.message : String(error)
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
this.daemonInitInProgress.delete(workspaceId);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
// Wait for all daemons to start (or fail)
|
|
139
|
+
await Promise.allSettled(startPromises);
|
|
140
|
+
this.logger.info('Chat Agent Daemons initialization complete', {
|
|
141
|
+
successCount: this.daemonManagers.size,
|
|
142
|
+
totalAttempted: workspacesWithOrchestrator.length
|
|
143
|
+
});
|
|
116
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Start Bug Monitor for all workspaces with running daemons
|
|
147
|
+
* Each workspace gets its own bug monitor instance
|
|
148
|
+
*/
|
|
117
149
|
async startBugMonitor() {
|
|
118
150
|
try {
|
|
119
|
-
// Get
|
|
151
|
+
// Get all configured accounts
|
|
120
152
|
const accounts = Object.entries(this.appConfig.hailerAccounts);
|
|
121
153
|
if (accounts.length === 0) {
|
|
122
154
|
this.logger.info('No Hailer accounts configured - Bug Reports Module disabled');
|
|
123
155
|
return;
|
|
124
156
|
}
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
157
|
+
// Start bug monitor for each workspace that has a running daemon
|
|
158
|
+
for (const [workspaceId, daemonManager] of this.daemonManagers) {
|
|
159
|
+
try {
|
|
160
|
+
// Find the appropriate API key for this workspace
|
|
161
|
+
// For now, use the first account (could be enhanced to match workspace-specific accounts)
|
|
162
|
+
const [apiKey] = accounts[0];
|
|
163
|
+
const userContext = await UserContextCache_1.UserContextCache.getContext(apiKey);
|
|
164
|
+
// Dynamic import - agents only needed when client is enabled
|
|
165
|
+
const { BugReportsModule } = require('./agents/bug-fixer');
|
|
166
|
+
const bugReportsModule = new BugReportsModule(userContext);
|
|
167
|
+
// Register bot user IDs to ignore (so bug monitor only processes human messages)
|
|
168
|
+
const daemonStatus = daemonManager.getStatus();
|
|
169
|
+
for (const daemon of daemonStatus) {
|
|
170
|
+
bugReportsModule.registerBotUser(daemon.botId);
|
|
171
|
+
this.logger.debug('Registered bot user for bug monitor to ignore', {
|
|
172
|
+
workspaceId,
|
|
173
|
+
botId: daemon.botId
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
// Register bug fixer disabled handler to trigger HAL response (BEFORE start!)
|
|
177
|
+
bugReportsModule.onBugFixerDisabled(async (bug) => {
|
|
178
|
+
if (bug.discussionId) {
|
|
179
|
+
const context = `[System notification: A new bug report "${bug.name}" was detected, but Bug Fixer (the auto-fix bot) is currently disabled. The user may ask how to enable Bug Fixer or want to know more about this bug. Be helpful and explain they can enable Bug Fixer in the AI Hub app.]`;
|
|
180
|
+
await daemonManager.triggerHalResponse(bug.discussionId, bug.id, context);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
await bugReportsModule.start();
|
|
184
|
+
if (bugReportsModule.isRunning()) {
|
|
185
|
+
this.bugReportsModules.set(workspaceId, bugReportsModule);
|
|
186
|
+
const config = bugReportsModule.getConfig();
|
|
187
|
+
this.logger.info('Bug Reports Module started', {
|
|
188
|
+
workspaceId,
|
|
189
|
+
autoFix: config.autoFix,
|
|
190
|
+
interval: `${config.intervalMs / 1000}s`
|
|
191
|
+
});
|
|
192
|
+
}
|
|
134
193
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
await this.daemonManager.triggerHalResponse(bug.discussionId, bug.id, context);
|
|
194
|
+
catch (error) {
|
|
195
|
+
this.logger.warn('Bug Reports Module failed to start for workspace - continuing with other workspaces', {
|
|
196
|
+
workspaceId,
|
|
197
|
+
error: error instanceof Error ? error.message : String(error)
|
|
198
|
+
});
|
|
141
199
|
}
|
|
142
|
-
});
|
|
143
|
-
await this.bugReportsModule.start();
|
|
144
|
-
if (this.bugReportsModule.isRunning()) {
|
|
145
|
-
const config = this.bugReportsModule.getConfig();
|
|
146
|
-
this.logger.info('Bug Reports Module started', {
|
|
147
|
-
autoFix: config.autoFix,
|
|
148
|
-
interval: `${config.intervalMs / 1000}s`
|
|
149
|
-
});
|
|
150
200
|
}
|
|
201
|
+
this.logger.info('Bug Reports Module initialization complete', {
|
|
202
|
+
successCount: this.bugReportsModules.size,
|
|
203
|
+
totalAttempted: this.daemonManagers.size
|
|
204
|
+
});
|
|
151
205
|
}
|
|
152
206
|
catch (error) {
|
|
153
207
|
this.logger.warn('Bug Reports Module failed to start - server will continue without bug monitoring', {
|
|
@@ -165,11 +219,28 @@ class Core {
|
|
|
165
219
|
}
|
|
166
220
|
const [apiKey] = accounts[0];
|
|
167
221
|
const userContext = await UserContextCache_1.UserContextCache.getContext(apiKey);
|
|
168
|
-
|
|
222
|
+
// Dynamic import - bot-config only needed when client is enabled
|
|
223
|
+
const { initBotConfigPersistence, onDaemonRestartNeeded } = require('./bot-config');
|
|
224
|
+
await initBotConfigPersistence(userContext.hailer);
|
|
169
225
|
this.logger.info('Bot config persistence initialized');
|
|
226
|
+
// Register webhook handler early so it can trigger daemon start
|
|
227
|
+
this.registerWebhookHandler();
|
|
170
228
|
// Register callback for daemon restart when new orchestrator is discovered
|
|
171
|
-
|
|
172
|
-
|
|
229
|
+
// This is called when orchestrator config changes in any workspace
|
|
230
|
+
onDaemonRestartNeeded(() => {
|
|
231
|
+
// Restart all workspaces with orchestrator enabled
|
|
232
|
+
const { loadBotConfigs } = require('./bot-config');
|
|
233
|
+
const configs = loadBotConfigs();
|
|
234
|
+
for (const ctx of configs) {
|
|
235
|
+
if (ctx.getOrchestratorCredentials()) {
|
|
236
|
+
this.attemptDaemonRestart(0, ctx.workspaceId).catch(err => {
|
|
237
|
+
this.logger.error('Failed to restart daemon after config change', {
|
|
238
|
+
workspaceId: ctx.workspaceId,
|
|
239
|
+
error: err
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
173
244
|
});
|
|
174
245
|
}
|
|
175
246
|
catch (error) {
|
|
@@ -178,49 +249,125 @@ class Core {
|
|
|
178
249
|
});
|
|
179
250
|
}
|
|
180
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* Register webhook handler for bot config updates
|
|
254
|
+
* Called early so webhooks can trigger daemon start even if initial start failed
|
|
255
|
+
*/
|
|
256
|
+
registerWebhookHandler() {
|
|
257
|
+
(0, webhook_handler_1.onBotUpdate)((workspaceId, bot, action) => {
|
|
258
|
+
this.logger.info('Webhook bot update received', { workspaceId, botType: bot.botType, action, enabled: bot.enabled });
|
|
259
|
+
if (bot.botType === 'orchestrator') {
|
|
260
|
+
if (action === 'remove') {
|
|
261
|
+
// Orchestrator disabled - stop daemon for this workspace only
|
|
262
|
+
this.logger.info('Orchestrator disabled via webhook, stopping daemon', { workspaceId });
|
|
263
|
+
const manager = this.daemonManagers.get(workspaceId);
|
|
264
|
+
if (manager) {
|
|
265
|
+
manager.stopAll().catch((err) => {
|
|
266
|
+
this.logger.error('Failed to stop daemons after orchestrator removal', { workspaceId, err });
|
|
267
|
+
}).then(() => {
|
|
268
|
+
// Clean up after stopping
|
|
269
|
+
this.daemonManagers.delete(workspaceId);
|
|
270
|
+
const interval = this.statusLogIntervals.get(workspaceId);
|
|
271
|
+
if (interval) {
|
|
272
|
+
clearInterval(interval);
|
|
273
|
+
this.statusLogIntervals.delete(workspaceId);
|
|
274
|
+
}
|
|
275
|
+
this.initialDaemonReady.delete(workspaceId);
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
// Orchestrator added/updated - start or restart daemon for this workspace
|
|
281
|
+
this.logger.info('Orchestrator config changed via webhook, triggering daemon start/restart', { workspaceId });
|
|
282
|
+
this.attemptDaemonRestart(0, workspaceId).catch(err => {
|
|
283
|
+
this.logger.error('Failed to restart daemon after orchestrator webhook update', { workspaceId, err });
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
// Specialist changed - hot-reload without losing orchestrator context
|
|
289
|
+
this.logger.info('Specialist config changed via webhook, hot-reloading', {
|
|
290
|
+
workspaceId,
|
|
291
|
+
botType: bot.botType,
|
|
292
|
+
enabled: bot.enabled
|
|
293
|
+
});
|
|
294
|
+
const manager = this.daemonManagers.get(workspaceId);
|
|
295
|
+
if (manager && bot.botType) {
|
|
296
|
+
manager.hotReloadSpecialist(bot.email, bot.password, bot.botType, bot.enabled, bot.userId || undefined).then((success) => {
|
|
297
|
+
if (success) {
|
|
298
|
+
this.logger.info('Specialist hot-reloaded successfully', {
|
|
299
|
+
workspaceId,
|
|
300
|
+
botType: bot.botType,
|
|
301
|
+
enabled: bot.enabled
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}).catch((err) => {
|
|
305
|
+
this.logger.error('Failed to hot-reload specialist', {
|
|
306
|
+
workspaceId,
|
|
307
|
+
botType: bot.botType,
|
|
308
|
+
error: err
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
this.logger.debug('Webhook handler registered');
|
|
315
|
+
}
|
|
181
316
|
/**
|
|
182
317
|
* Attempt to restart daemon mode when bot state changes
|
|
183
|
-
* Stops existing
|
|
318
|
+
* Stops existing daemon for a specific workspace and starts fresh with updated configuration
|
|
184
319
|
* Includes retry logic for transient connection failures
|
|
320
|
+
*
|
|
321
|
+
* @param retryCount - Number of retry attempts made so far
|
|
322
|
+
* @param workspaceId - REQUIRED workspace ID to restart daemon for
|
|
185
323
|
*/
|
|
186
324
|
async attemptDaemonRestart(retryCount = 0, workspaceId) {
|
|
187
325
|
const MAX_RETRIES = 3;
|
|
188
326
|
const RETRY_DELAY_MS = 5000;
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
327
|
+
// If initial daemon for this workspace never started, check if we now have config to start it
|
|
328
|
+
if (!this.initialDaemonReady.has(workspaceId)) {
|
|
329
|
+
// Check if config now exists for this workspace
|
|
330
|
+
const { loadBotConfigs } = require('./bot-config');
|
|
331
|
+
const configs = loadBotConfigs();
|
|
332
|
+
const workspaceConfig = configs.find((ctx) => ctx.workspaceId === workspaceId && ctx.getOrchestratorCredentials());
|
|
333
|
+
if (!workspaceConfig) {
|
|
334
|
+
this.logger.debug('Skipping daemon start - no orchestrator config yet', { workspaceId });
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
this.logger.info('Config now available, attempting first daemon start', { workspaceId });
|
|
338
|
+
// Continue to start daemon for first time
|
|
194
339
|
}
|
|
195
|
-
if (this.daemonInitInProgress) {
|
|
196
|
-
this.logger.debug('Daemon init already in progress, skipping');
|
|
340
|
+
if (this.daemonInitInProgress.has(workspaceId)) {
|
|
341
|
+
this.logger.debug('Daemon init already in progress, skipping', { workspaceId });
|
|
197
342
|
return;
|
|
198
343
|
}
|
|
199
|
-
this.logger.info('Restarting
|
|
200
|
-
this.daemonInitInProgress
|
|
344
|
+
this.logger.info('Restarting daemon after bot state change', { workspaceId, retryCount });
|
|
345
|
+
this.daemonInitInProgress.add(workspaceId);
|
|
201
346
|
try {
|
|
202
|
-
// Stop existing
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
347
|
+
// Stop existing daemon for this workspace if running
|
|
348
|
+
const existingManager = this.daemonManagers.get(workspaceId);
|
|
349
|
+
if (existingManager) {
|
|
350
|
+
this.logger.info('Stopping existing daemon for restart', { workspaceId });
|
|
351
|
+
const interval = this.statusLogIntervals.get(workspaceId);
|
|
352
|
+
if (interval) {
|
|
353
|
+
clearInterval(interval);
|
|
354
|
+
this.statusLogIntervals.delete(workspaceId);
|
|
208
355
|
}
|
|
209
|
-
await
|
|
210
|
-
this.
|
|
356
|
+
await existingManager.stopAll();
|
|
357
|
+
this.daemonManagers.delete(workspaceId);
|
|
211
358
|
}
|
|
212
|
-
// Start fresh with updated bot state
|
|
359
|
+
// Start fresh with updated bot state for this workspace
|
|
213
360
|
await this.initializeDaemonMode(workspaceId);
|
|
214
|
-
// Verify
|
|
215
|
-
|
|
216
|
-
const newManager = this.daemonManager;
|
|
361
|
+
// Verify daemon was created successfully
|
|
362
|
+
const newManager = this.daemonManagers.get(workspaceId);
|
|
217
363
|
if (!newManager) {
|
|
218
364
|
throw new Error('Daemon manager was not created');
|
|
219
365
|
}
|
|
220
366
|
if (!newManager.getOrchestrator()) {
|
|
221
367
|
throw new Error('Daemon manager created but no orchestrator available');
|
|
222
368
|
}
|
|
223
|
-
this.logger.info('
|
|
369
|
+
this.logger.info('Daemon successfully restarted after state change', { workspaceId });
|
|
370
|
+
this.initialDaemonReady.add(workspaceId);
|
|
224
371
|
}
|
|
225
372
|
catch (error) {
|
|
226
373
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -229,110 +376,95 @@ class Core {
|
|
|
229
376
|
errorMessage.includes('No orchestrator');
|
|
230
377
|
if (isTransientError && retryCount < MAX_RETRIES) {
|
|
231
378
|
this.logger.warn('Daemon restart failed with transient error, scheduling retry', {
|
|
379
|
+
workspaceId,
|
|
232
380
|
error: errorMessage,
|
|
233
381
|
retryCount: retryCount + 1,
|
|
234
382
|
retryDelayMs: RETRY_DELAY_MS
|
|
235
383
|
});
|
|
236
384
|
// Reset flag before scheduling retry
|
|
237
|
-
this.daemonInitInProgress
|
|
385
|
+
this.daemonInitInProgress.delete(workspaceId);
|
|
238
386
|
setTimeout(() => {
|
|
239
387
|
this.attemptDaemonRestart(retryCount + 1, workspaceId);
|
|
240
388
|
}, RETRY_DELAY_MS);
|
|
241
389
|
return;
|
|
242
390
|
}
|
|
243
391
|
this.logger.warn('Daemon restart failed', {
|
|
392
|
+
workspaceId,
|
|
244
393
|
error: errorMessage,
|
|
245
394
|
retriesExhausted: retryCount >= MAX_RETRIES
|
|
246
395
|
});
|
|
247
396
|
}
|
|
248
397
|
finally {
|
|
249
|
-
this.daemonInitInProgress
|
|
398
|
+
this.daemonInitInProgress.delete(workspaceId);
|
|
250
399
|
}
|
|
251
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* Initialize daemon for a specific workspace
|
|
403
|
+
*
|
|
404
|
+
* @param workspaceId - REQUIRED workspace ID to initialize daemon for
|
|
405
|
+
*/
|
|
252
406
|
async initializeDaemonMode(workspaceId) {
|
|
253
407
|
this.logger.info('Initializing Chat Agent Daemon', { workspaceId });
|
|
254
408
|
// Check for orchestrator mode via environment variable
|
|
255
|
-
const orchestratorMode = process.env.DAEMON_ORCHESTRATOR_MODE
|
|
256
|
-
this.logger.info(`Daemon mode: ${orchestratorMode ? 'ORCHESTRATOR' : 'STANDARD'}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
409
|
+
const orchestratorMode = process.env.DAEMON_ORCHESTRATOR_MODE !== 'false';
|
|
410
|
+
this.logger.info(`Daemon mode: ${orchestratorMode ? 'ORCHESTRATOR' : 'STANDARD'}`, { workspaceId });
|
|
411
|
+
// Dynamic import - agents only needed when client is enabled
|
|
412
|
+
const { createDaemonManager } = require('./agents/factory');
|
|
413
|
+
const daemonManager = await createDaemonManager({ orchestratorMode, workspaceId });
|
|
414
|
+
if (!daemonManager) {
|
|
415
|
+
throw new Error(`Failed to create daemon manager for workspace ${workspaceId}`);
|
|
260
416
|
}
|
|
261
|
-
|
|
417
|
+
// Store the manager for this workspace
|
|
418
|
+
this.daemonManagers.set(workspaceId, daemonManager);
|
|
419
|
+
const status = daemonManager.getStatus();
|
|
262
420
|
this.logger.info('Chat Agent Daemon initialized and ready', {
|
|
421
|
+
workspaceId,
|
|
263
422
|
daemonCount: status.length,
|
|
264
|
-
bots: status.map(s => s.botId),
|
|
423
|
+
bots: status.map((s) => s.botId),
|
|
265
424
|
orchestratorMode
|
|
266
425
|
});
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (action === 'remove') {
|
|
272
|
-
// Orchestrator disabled - stop daemons, don't restart
|
|
273
|
-
this.logger.info('Orchestrator disabled via webhook, stopping daemons');
|
|
274
|
-
this.daemonManager?.stopAll().catch(err => {
|
|
275
|
-
this.logger.error('Failed to stop daemons after orchestrator removal', err);
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
// Orchestrator added/updated - restart daemons
|
|
280
|
-
this.logger.info('Orchestrator config changed via webhook, triggering daemon restart');
|
|
281
|
-
this.attemptDaemonRestart(0, workspaceId).catch(err => {
|
|
282
|
-
this.logger.error('Failed to restart daemon after orchestrator webhook update', err);
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
// Specialist changed - hot-reload without losing orchestrator context
|
|
288
|
-
this.logger.info('Specialist config changed via webhook, hot-reloading', {
|
|
289
|
-
botType: bot.botType,
|
|
290
|
-
enabled: bot.enabled
|
|
291
|
-
});
|
|
292
|
-
if (this.daemonManager && bot.botType) {
|
|
293
|
-
this.daemonManager.hotReloadSpecialist(bot.email, bot.password, bot.botType, bot.enabled, bot.userId || undefined).then(success => {
|
|
294
|
-
if (success) {
|
|
295
|
-
this.logger.info('Specialist hot-reloaded successfully', {
|
|
296
|
-
botType: bot.botType,
|
|
297
|
-
enabled: bot.enabled
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
this.logger.warn('Specialist hot-reload failed', { botType: bot.botType });
|
|
302
|
-
}
|
|
303
|
-
}).catch(err => {
|
|
304
|
-
this.logger.error('Error during specialist hot-reload', err);
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
// Start periodic status logging (every 60s)
|
|
310
|
-
this.statusLogInterval = this.daemonManager.startStatusLogging(60000);
|
|
311
|
-
this.logger.info('Daemon status logging started (every 60s)');
|
|
426
|
+
// Start periodic status logging (every 60s) for this workspace
|
|
427
|
+
const interval = daemonManager.startStatusLogging(60000);
|
|
428
|
+
this.statusLogIntervals.set(workspaceId, interval);
|
|
429
|
+
this.logger.info('Daemon status logging started (every 60s)', { workspaceId });
|
|
312
430
|
}
|
|
313
431
|
/**
|
|
314
|
-
* Get daemon status (for HTTP endpoint)
|
|
432
|
+
* Get daemon status for all workspaces (for HTTP endpoint)
|
|
315
433
|
*/
|
|
316
434
|
getDaemonStatus() {
|
|
317
|
-
|
|
435
|
+
const allStatus = {};
|
|
436
|
+
for (const [workspaceId, manager] of this.daemonManagers) {
|
|
437
|
+
allStatus[workspaceId] = manager.getStatus();
|
|
438
|
+
}
|
|
439
|
+
return allStatus;
|
|
318
440
|
}
|
|
319
441
|
async stop() {
|
|
320
442
|
this.logger.info('Stopping Hailer MCP application');
|
|
321
443
|
try {
|
|
322
|
-
// Stop status logging
|
|
323
|
-
|
|
324
|
-
clearInterval(
|
|
444
|
+
// Stop all status logging intervals
|
|
445
|
+
for (const [workspaceId, interval] of this.statusLogIntervals) {
|
|
446
|
+
clearInterval(interval);
|
|
447
|
+
this.logger.debug('Stopped status logging', { workspaceId });
|
|
325
448
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
449
|
+
this.statusLogIntervals.clear();
|
|
450
|
+
// Stop all Bug Reports Modules
|
|
451
|
+
for (const [workspaceId, module] of this.bugReportsModules) {
|
|
452
|
+
await module.stop();
|
|
453
|
+
this.logger.debug('Stopped Bug Reports Module', { workspaceId });
|
|
329
454
|
}
|
|
330
|
-
|
|
331
|
-
(
|
|
332
|
-
if (this.
|
|
333
|
-
|
|
334
|
-
|
|
455
|
+
this.bugReportsModules.clear();
|
|
456
|
+
// Cleanup bot config (clear timers and callbacks) - only if client was enabled
|
|
457
|
+
if (this.appConfig.server.enableClient) {
|
|
458
|
+
const { cleanupBotConfig } = require('./bot-config');
|
|
459
|
+
cleanupBotConfig();
|
|
335
460
|
}
|
|
461
|
+
// Stop all daemon managers
|
|
462
|
+
const stopPromises = Array.from(this.daemonManagers.entries()).map(async ([workspaceId, manager]) => {
|
|
463
|
+
await manager.stopAll();
|
|
464
|
+
this.logger.info('Chat Agent Daemon stopped', { workspaceId });
|
|
465
|
+
});
|
|
466
|
+
await Promise.all(stopPromises);
|
|
467
|
+
this.daemonManagers.clear();
|
|
336
468
|
if (this.mcpServer) {
|
|
337
469
|
await this.mcpServer.stop();
|
|
338
470
|
this.logger.info('MCP Server stopped');
|
|
@@ -105,6 +105,24 @@ class UserContextCache {
|
|
|
105
105
|
const init = await client.socket.request('v2.core.init', [
|
|
106
106
|
['processes', 'users', 'network', 'networks', 'teams']
|
|
107
107
|
]);
|
|
108
|
+
// Validate single workspace access - MCP requires bot credentials with access to one workspace only
|
|
109
|
+
const workspaceCount = Object.keys(init.networks || {}).length;
|
|
110
|
+
if (workspaceCount > 1) {
|
|
111
|
+
const networks = (init.networks || {});
|
|
112
|
+
const workspaceNames = Object.values(networks)
|
|
113
|
+
.map((ws) => ws.name)
|
|
114
|
+
.join(', ');
|
|
115
|
+
logger.error('Multi-workspace credentials detected', {
|
|
116
|
+
workspaceCount,
|
|
117
|
+
workspaces: workspaceNames,
|
|
118
|
+
apiKey: apiKey.substring(0, 8) + '...'
|
|
119
|
+
});
|
|
120
|
+
// Clean up the connection before throwing - prevents dangling socket
|
|
121
|
+
(0, hailer_clients_1.disconnectHailerClientByApiKey)(apiKey);
|
|
122
|
+
throw new Error(`Multi-workspace credentials detected (${workspaceCount} workspaces: ${workspaceNames}). ` +
|
|
123
|
+
`MCP requires bot credentials with access to a single workspace. ` +
|
|
124
|
+
`Please use the bot account created during 'hailer-sdk init'.`);
|
|
125
|
+
}
|
|
108
126
|
// Create workspace cache from init data
|
|
109
127
|
const appConfig = (0, config_1.createApplicationConfig)();
|
|
110
128
|
const workspaceCache = (0, workspace_cache_1.createWorkspaceCache)(init, appConfig.mcpConfig);
|
|
@@ -55,8 +55,16 @@ export declare const subscribeToSignal: (apiKey: string, eventType: SignalType |
|
|
|
55
55
|
/**
|
|
56
56
|
* Register new bot credentials for dynamic bot creation
|
|
57
57
|
* Returns an API key that can be used to create a connection
|
|
58
|
+
*
|
|
59
|
+
* @param botId - Unique bot identifier
|
|
60
|
+
* @param email - Bot's Hailer email
|
|
61
|
+
* @param password - Bot's Hailer password
|
|
62
|
+
* @param options - Optional configuration
|
|
63
|
+
* @param options.allowedGroups - Tool groups this bot can access (e.g., ['read', 'write', 'bot_internal'])
|
|
58
64
|
*/
|
|
59
|
-
export declare function registerBotCredentials(botId: string, email: string, password: string
|
|
65
|
+
export declare function registerBotCredentials(botId: string, email: string, password: string, options?: {
|
|
66
|
+
allowedGroups?: string[];
|
|
67
|
+
}): string;
|
|
60
68
|
/**
|
|
61
69
|
* Unregister bot credentials and disconnect
|
|
62
70
|
*/
|
|
@@ -332,16 +332,26 @@ exports.subscribeToSignal = subscribeToSignal;
|
|
|
332
332
|
/**
|
|
333
333
|
* Register new bot credentials for dynamic bot creation
|
|
334
334
|
* Returns an API key that can be used to create a connection
|
|
335
|
+
*
|
|
336
|
+
* @param botId - Unique bot identifier
|
|
337
|
+
* @param email - Bot's Hailer email
|
|
338
|
+
* @param password - Bot's Hailer password
|
|
339
|
+
* @param options - Optional configuration
|
|
340
|
+
* @param options.allowedGroups - Tool groups this bot can access (e.g., ['read', 'write', 'bot_internal'])
|
|
335
341
|
*/
|
|
336
|
-
function registerBotCredentials(botId, email, password) {
|
|
342
|
+
function registerBotCredentials(botId, email, password, options) {
|
|
337
343
|
const apiKey = `bot-${botId}-${Date.now()}`;
|
|
338
|
-
// Add to environment CLIENT_CONFIGS
|
|
344
|
+
// Add to environment CLIENT_CONFIGS with optional tool access config
|
|
339
345
|
config_1.environment.CLIENT_CONFIGS[apiKey] = {
|
|
340
346
|
email,
|
|
341
347
|
password,
|
|
342
348
|
apiBaseUrl: 'https://api.hailer.com',
|
|
349
|
+
...(options?.allowedGroups && { allowedGroups: options.allowedGroups }),
|
|
343
350
|
};
|
|
344
|
-
logger.info('Bot credentials registered', {
|
|
351
|
+
logger.info('Bot credentials registered', {
|
|
352
|
+
botId,
|
|
353
|
+
hasAllowedGroups: !!options?.allowedGroups
|
|
354
|
+
});
|
|
345
355
|
return apiKey;
|
|
346
356
|
}
|
|
347
357
|
/**
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SignalHandler = void 0;
|
|
4
4
|
const logger_1 = require("../lib/logger");
|
|
5
|
-
const bot_config_1 = require("
|
|
5
|
+
const bot_config_1 = require("../bot-config");
|
|
6
6
|
const logger = (0, logger_1.createLogger)({ component: 'signal-handler' });
|
|
7
7
|
class SignalHandler {
|
|
8
8
|
client;
|