@hailer/mcp 0.1.14 → 0.1.16
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/.claude/agents/agent-giuseppe-app-builder.md +7 -6
- package/.claude/agents/agent-lars-code-inspector.md +26 -14
- package/dist/agents/bot-manager.d.ts +48 -0
- package/dist/agents/bot-manager.js +254 -0
- package/dist/agents/factory.d.ts +150 -0
- package/dist/agents/factory.js +650 -0
- package/dist/agents/giuseppe/ai.d.ts +83 -0
- package/dist/agents/giuseppe/ai.js +466 -0
- package/dist/agents/giuseppe/bot.d.ts +110 -0
- package/dist/agents/giuseppe/bot.js +780 -0
- package/dist/agents/giuseppe/config.d.ts +25 -0
- package/dist/agents/giuseppe/config.js +227 -0
- package/dist/agents/giuseppe/files.d.ts +52 -0
- package/dist/agents/giuseppe/files.js +338 -0
- package/dist/agents/giuseppe/git.d.ts +48 -0
- package/dist/agents/giuseppe/git.js +298 -0
- package/dist/agents/giuseppe/index.d.ts +97 -0
- package/dist/agents/giuseppe/index.js +258 -0
- package/dist/agents/giuseppe/lsp.d.ts +113 -0
- package/dist/agents/giuseppe/lsp.js +485 -0
- package/dist/agents/giuseppe/monitor.d.ts +118 -0
- package/dist/agents/giuseppe/monitor.js +621 -0
- package/dist/agents/giuseppe/prompt.d.ts +5 -0
- package/dist/agents/giuseppe/prompt.js +94 -0
- package/dist/agents/giuseppe/registries/pending-classification.d.ts +28 -0
- package/dist/agents/giuseppe/registries/pending-classification.js +50 -0
- package/dist/agents/giuseppe/registries/pending-fix.d.ts +30 -0
- package/dist/agents/giuseppe/registries/pending-fix.js +42 -0
- package/dist/agents/giuseppe/registries/pending.d.ts +27 -0
- package/dist/agents/giuseppe/registries/pending.js +49 -0
- package/dist/agents/giuseppe/specialist.d.ts +47 -0
- package/dist/agents/giuseppe/specialist.js +237 -0
- package/dist/agents/giuseppe/types.d.ts +123 -0
- package/dist/agents/giuseppe/types.js +9 -0
- package/dist/agents/hailer-expert/index.d.ts +8 -0
- package/dist/agents/hailer-expert/index.js +14 -0
- package/dist/agents/hal/daemon.d.ts +142 -0
- package/dist/agents/hal/daemon.js +1103 -0
- package/dist/agents/hal/definitions.d.ts +55 -0
- package/dist/agents/hal/definitions.js +263 -0
- package/dist/agents/hal/index.d.ts +3 -0
- package/dist/agents/hal/index.js +8 -0
- package/dist/agents/index.d.ts +18 -0
- package/dist/agents/index.js +48 -0
- package/dist/agents/shared/base.d.ts +216 -0
- package/dist/agents/shared/base.js +846 -0
- package/dist/agents/shared/services/agent-registry.d.ts +107 -0
- package/dist/agents/shared/services/agent-registry.js +629 -0
- package/dist/agents/shared/services/conversation-manager.d.ts +50 -0
- package/dist/agents/shared/services/conversation-manager.js +136 -0
- package/dist/agents/shared/services/mcp-client.d.ts +56 -0
- package/dist/agents/shared/services/mcp-client.js +124 -0
- package/dist/agents/shared/services/message-classifier.d.ts +37 -0
- package/dist/agents/shared/services/message-classifier.js +187 -0
- package/dist/agents/shared/services/message-formatter.d.ts +89 -0
- package/dist/agents/shared/services/message-formatter.js +371 -0
- package/dist/agents/shared/services/session-logger.d.ts +106 -0
- package/dist/agents/shared/services/session-logger.js +446 -0
- package/dist/agents/shared/services/tool-executor.d.ts +41 -0
- package/dist/agents/shared/services/tool-executor.js +169 -0
- package/dist/agents/shared/services/workspace-schema-cache.d.ts +125 -0
- package/dist/agents/shared/services/workspace-schema-cache.js +578 -0
- package/dist/agents/shared/specialist.d.ts +91 -0
- package/dist/agents/shared/specialist.js +399 -0
- package/dist/agents/shared/tool-schema-loader.d.ts +62 -0
- package/dist/agents/shared/tool-schema-loader.js +232 -0
- package/dist/agents/shared/types.d.ts +327 -0
- package/dist/agents/shared/types.js +121 -0
- package/dist/app.js +21 -4
- package/dist/cli.js +0 -0
- package/dist/client/agents/orchestrator.d.ts +1 -0
- package/dist/client/agents/orchestrator.js +12 -1
- package/dist/commands/seed-config.d.ts +9 -0
- package/dist/commands/seed-config.js +372 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.js +61 -1
- package/dist/core.d.ts +8 -0
- package/dist/core.js +137 -6
- package/dist/lib/discussion-lock.d.ts +42 -0
- package/dist/lib/discussion-lock.js +110 -0
- package/dist/mcp/UserContextCache.js +2 -2
- package/dist/mcp/hailer-clients.d.ts +15 -0
- package/dist/mcp/hailer-clients.js +100 -6
- package/dist/mcp/signal-handler.d.ts +16 -5
- package/dist/mcp/signal-handler.js +173 -122
- package/dist/mcp/tools/activity.js +9 -1
- package/dist/mcp/tools/bot-config.d.ts +184 -9
- package/dist/mcp/tools/bot-config.js +2177 -163
- package/dist/mcp/tools/giuseppe-tools.d.ts +21 -0
- package/dist/mcp/tools/giuseppe-tools.js +525 -0
- package/dist/mcp/utils/hailer-api-client.d.ts +42 -1
- package/dist/mcp/utils/hailer-api-client.js +128 -2
- package/dist/mcp/webhook-handler.d.ts +87 -0
- package/dist/mcp/webhook-handler.js +343 -0
- package/dist/mcp/workspace-cache.d.ts +5 -0
- package/dist/mcp/workspace-cache.js +11 -0
- package/dist/mcp-server.js +55 -5
- package/dist/modules/bug-reports/giuseppe-agent.d.ts +58 -0
- package/dist/modules/bug-reports/giuseppe-agent.js +467 -0
- package/dist/modules/bug-reports/giuseppe-ai.d.ts +25 -1
- package/dist/modules/bug-reports/giuseppe-ai.js +133 -2
- package/dist/modules/bug-reports/giuseppe-bot.d.ts +3 -2
- package/dist/modules/bug-reports/giuseppe-bot.js +75 -36
- package/dist/modules/bug-reports/giuseppe-daemon.d.ts +80 -0
- package/dist/modules/bug-reports/giuseppe-daemon.js +617 -0
- package/dist/modules/bug-reports/giuseppe-files.d.ts +12 -0
- package/dist/modules/bug-reports/giuseppe-files.js +37 -0
- package/dist/modules/bug-reports/giuseppe-lsp.d.ts +113 -0
- package/dist/modules/bug-reports/giuseppe-lsp.js +485 -0
- package/dist/modules/bug-reports/index.d.ts +1 -0
- package/dist/modules/bug-reports/index.js +31 -29
- package/package.json +5 -4
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const zod_1 = require("zod");
|
|
3
|
+
exports.SignalHandler = void 0;
|
|
5
4
|
const logger_1 = require("../lib/logger");
|
|
5
|
+
const bot_config_1 = require("./tools/bot-config");
|
|
6
6
|
const logger = (0, logger_1.createLogger)({ component: 'signal-handler' });
|
|
7
7
|
class SignalHandler {
|
|
8
8
|
client;
|
|
@@ -10,16 +10,41 @@ class SignalHandler {
|
|
|
10
10
|
subscriptions = new Map();
|
|
11
11
|
signalHistory = [];
|
|
12
12
|
maxHistorySize = 100;
|
|
13
|
+
// Static cache to prevent duplicate handlers per socket
|
|
14
|
+
static handlersBySocketId = new Map();
|
|
13
15
|
constructor(client, workspaceCache) {
|
|
14
16
|
this.client = client;
|
|
15
17
|
this.workspaceCache = workspaceCache;
|
|
16
18
|
this.initializeSignalHandling();
|
|
17
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Get or create a SignalHandler for a client (prevents duplicate listeners)
|
|
22
|
+
*/
|
|
23
|
+
static getOrCreate(client, workspaceCache) {
|
|
24
|
+
// Use session key as unique identifier - each HailerClient has a unique sessionKey
|
|
25
|
+
const sessionKey = client.sessionKey;
|
|
26
|
+
const existing = SignalHandler.handlersBySocketId.get(sessionKey);
|
|
27
|
+
if (existing) {
|
|
28
|
+
logger.debug('Reusing existing SignalHandler', { sessionKey });
|
|
29
|
+
return existing;
|
|
30
|
+
}
|
|
31
|
+
const handler = new SignalHandler(client, workspaceCache);
|
|
32
|
+
SignalHandler.handlersBySocketId.set(sessionKey, handler);
|
|
33
|
+
logger.debug('Created new SignalHandler', { sessionKey });
|
|
34
|
+
return handler;
|
|
35
|
+
}
|
|
36
|
+
signalListener = null;
|
|
18
37
|
initializeSignalHandling() {
|
|
19
|
-
//
|
|
20
|
-
this.
|
|
38
|
+
// Remove any existing listener first to prevent duplicates
|
|
39
|
+
if (this.signalListener) {
|
|
40
|
+
this.client.socket.off('signals', this.signalListener);
|
|
41
|
+
}
|
|
42
|
+
// Create and store the listener reference
|
|
43
|
+
this.signalListener = (signal) => {
|
|
21
44
|
this.handleIncomingSignal(signal);
|
|
22
|
-
}
|
|
45
|
+
};
|
|
46
|
+
// Register for all signals from the socket client
|
|
47
|
+
this.client.socket.on('signals', this.signalListener);
|
|
23
48
|
}
|
|
24
49
|
handleIncomingSignal(rawSignal) {
|
|
25
50
|
try {
|
|
@@ -37,16 +62,13 @@ class SignalHandler {
|
|
|
37
62
|
currentWorkspaceId: this.workspaceCache.currentWorkspace._id,
|
|
38
63
|
});
|
|
39
64
|
}
|
|
40
|
-
// Debug: log
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
workspaceId: String(data.workspaceId || ''),
|
|
48
|
-
});
|
|
49
|
-
}
|
|
65
|
+
// Debug: log ALL incoming signals to discover what's available
|
|
66
|
+
const data = eventData;
|
|
67
|
+
logger.debug('Signal received', {
|
|
68
|
+
eventType,
|
|
69
|
+
dataKeys: Object.keys(eventData),
|
|
70
|
+
sid: String(data.sid || ''),
|
|
71
|
+
});
|
|
50
72
|
const signal = {
|
|
51
73
|
type: eventType,
|
|
52
74
|
data: eventData,
|
|
@@ -108,27 +130,71 @@ class SignalHandler {
|
|
|
108
130
|
case 'cache.invalidate':
|
|
109
131
|
this.handleCacheInvalidate(signal);
|
|
110
132
|
break;
|
|
133
|
+
case 'company.new_invitation':
|
|
134
|
+
this.handleNewInvitation(signal);
|
|
135
|
+
break;
|
|
111
136
|
default:
|
|
112
137
|
logger.debug('Unhandled signal type received', { signalType: signal.type });
|
|
113
138
|
}
|
|
114
139
|
}
|
|
115
140
|
handleActivityUpdated(signal) {
|
|
141
|
+
const data = signal.data;
|
|
142
|
+
// Check for phase change data - try direct properties first, then meta (for backwards compatibility)
|
|
143
|
+
const processId = data.processId || data.meta?.processId;
|
|
144
|
+
const phase = data.phase || data.meta?.phase;
|
|
145
|
+
const activityIdRaw = data.activity_id || data.meta?.activity_id;
|
|
146
|
+
// Log full signal data to debug phase change detection
|
|
147
|
+
logger.debug('Activity updated signal - FULL DATA', {
|
|
148
|
+
signalDataKeys: Object.keys(signal.data || {}),
|
|
149
|
+
meta: data.meta ? JSON.stringify(data.meta) : 'undefined',
|
|
150
|
+
hasActivityId: !!activityIdRaw,
|
|
151
|
+
hasProcessId: !!processId,
|
|
152
|
+
hasPhase: !!phase,
|
|
153
|
+
});
|
|
116
154
|
logger.debug('Activity updated signal received', {
|
|
117
|
-
activityId: signal.data
|
|
118
|
-
workspaceId: signal.workspaceId
|
|
155
|
+
activityId: signal.data._id || signal.data.id,
|
|
156
|
+
workspaceId: signal.workspaceId,
|
|
157
|
+
processId,
|
|
158
|
+
phase,
|
|
159
|
+
prevPhase: data.prevPhase || data.meta?.prevPhase
|
|
119
160
|
});
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
// this
|
|
161
|
+
// Handle Agent Directory phase changes for bot enable/disable
|
|
162
|
+
// DISABLED: Now using webhook instead of socket signals for Agent Directory updates
|
|
163
|
+
// The webhook handler (src/mcp/webhook-handler.ts) handles bot config updates
|
|
164
|
+
// Keeping this code for potential fallback if webhook is unavailable
|
|
165
|
+
// if (processId && phase && activityIdRaw) {
|
|
166
|
+
// const activityIds = Array.isArray(activityIdRaw)
|
|
167
|
+
// ? activityIdRaw
|
|
168
|
+
// : [activityIdRaw];
|
|
169
|
+
//
|
|
170
|
+
// handleActivityPhaseChange(processId, activityIds, phase).catch(err => {
|
|
171
|
+
// logger.error('Failed to handle activity phase change', err);
|
|
172
|
+
// });
|
|
173
|
+
// }
|
|
124
174
|
}
|
|
125
175
|
handleActivityCreated(signal) {
|
|
176
|
+
const data = signal.data;
|
|
177
|
+
// Extract data - try direct properties first, then meta
|
|
178
|
+
const processId = data.processId || data.meta?.processId;
|
|
179
|
+
const phase = data.phase || data.meta?.phase;
|
|
180
|
+
const activityIdRaw = data.activity_id || data.meta?.activity_id || data._id || data.id;
|
|
126
181
|
logger.debug('Activity created signal received', {
|
|
127
|
-
activityId:
|
|
128
|
-
workspaceId: signal.workspaceId
|
|
182
|
+
activityId: Array.isArray(activityIdRaw) ? activityIdRaw.join(',') : activityIdRaw,
|
|
183
|
+
workspaceId: signal.workspaceId,
|
|
184
|
+
processId,
|
|
185
|
+
phase,
|
|
129
186
|
});
|
|
130
|
-
//
|
|
131
|
-
//
|
|
187
|
+
// Handle Agent Directory discovery - when a new bot is added, discover schema
|
|
188
|
+
// DISABLED: Now using webhook instead of socket signals for Agent Directory updates
|
|
189
|
+
// if (processId && phase && activityIdRaw) {
|
|
190
|
+
// const activityIds = Array.isArray(activityIdRaw)
|
|
191
|
+
// ? activityIdRaw
|
|
192
|
+
// : [activityIdRaw];
|
|
193
|
+
//
|
|
194
|
+
// handleActivityPhaseChange(processId, activityIds, phase).catch(err => {
|
|
195
|
+
// logger.error('Failed to handle activity creation for bot discovery', err);
|
|
196
|
+
// });
|
|
197
|
+
// }
|
|
132
198
|
}
|
|
133
199
|
handleDiscussionMessage(signal) {
|
|
134
200
|
logger.debug('Discussion message signal received', {
|
|
@@ -163,13 +229,28 @@ class SignalHandler {
|
|
|
163
229
|
});
|
|
164
230
|
try {
|
|
165
231
|
// Refresh cache by fetching fresh data - API requires array of keys to fetch
|
|
166
|
-
|
|
232
|
+
// IMPORTANT: Include 'processes' to get updated workflow list (e.g., after template install)
|
|
233
|
+
const init = await this.client.socket.request('v2.core.init', [['users', 'network', 'networks', 'processes']]);
|
|
167
234
|
// Update workspace cache with fresh data
|
|
168
235
|
if (init.processes) {
|
|
169
236
|
this.workspaceCache.rawInit.processes = init.processes;
|
|
170
237
|
logger.info('Workflows cache refreshed', {
|
|
171
238
|
workflowCount: init.processes.length
|
|
172
239
|
});
|
|
240
|
+
// Check if Agent Directory workflow now exists (hot-load after template install)
|
|
241
|
+
// This enables the daemon to start automatically when AI Hub template is installed
|
|
242
|
+
const agentDirectoryPatterns = ['agent directory', 'ai agents', '🤖 agent directory'];
|
|
243
|
+
const hasAgentDirectory = init.processes.some((p) => {
|
|
244
|
+
const name = (p.name || '').toLowerCase();
|
|
245
|
+
return agentDirectoryPatterns.some(pattern => name.includes(pattern));
|
|
246
|
+
});
|
|
247
|
+
if (hasAgentDirectory) {
|
|
248
|
+
logger.info('Agent Directory workflow detected after cache refresh, triggering bot config reload');
|
|
249
|
+
// Async reload - don't await to avoid blocking signal handling
|
|
250
|
+
(0, bot_config_1.reloadConfigFromHailer)().catch(err => {
|
|
251
|
+
logger.warn('Bot config reload failed after Agent Directory detection', { error: err });
|
|
252
|
+
});
|
|
253
|
+
}
|
|
173
254
|
}
|
|
174
255
|
if (init.users) {
|
|
175
256
|
// Store raw users in rawInit - the users array is derived from this
|
|
@@ -193,6 +274,72 @@ class SignalHandler {
|
|
|
193
274
|
logger.error('Failed to refresh cache after invalidation signal', error);
|
|
194
275
|
}
|
|
195
276
|
}
|
|
277
|
+
async handleNewInvitation(signal) {
|
|
278
|
+
logger.info('New workspace invitation received', {
|
|
279
|
+
workspaceId: signal.workspaceId
|
|
280
|
+
});
|
|
281
|
+
// Auto-accept all pending invitations
|
|
282
|
+
try {
|
|
283
|
+
// Get pending invites
|
|
284
|
+
const invites = await this.client.socket.request('v2.user.invite.list', []);
|
|
285
|
+
if (!invites || invites.length === 0) {
|
|
286
|
+
logger.debug('No pending invitations to accept');
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
logger.info('Found pending invitations', { count: invites.length });
|
|
290
|
+
// Accept each invitation
|
|
291
|
+
for (const invite of invites) {
|
|
292
|
+
try {
|
|
293
|
+
await this.client.socket.request('v2.user.invite.accept', [invite.invite_key]);
|
|
294
|
+
logger.info('Auto-accepted workspace invitation', {
|
|
295
|
+
workspaceId: invite.cid,
|
|
296
|
+
workspaceName: invite.network?.name
|
|
297
|
+
});
|
|
298
|
+
// Switch to the new workspace so the existing automation can discover Agent Directory
|
|
299
|
+
try {
|
|
300
|
+
logger.info('Switching to new workspace', { workspaceId: invite.cid });
|
|
301
|
+
await this.client.socket.request('core.switch_ecosystem', [invite.cid]);
|
|
302
|
+
logger.info('Switched to workspace successfully', { workspaceId: invite.cid });
|
|
303
|
+
// Update active workspace for bot-config
|
|
304
|
+
(0, bot_config_1.setActiveWorkspace)(invite.cid);
|
|
305
|
+
// Trigger Agent Directory discovery and bot initialization
|
|
306
|
+
try {
|
|
307
|
+
logger.info('Triggering bot config reload for new workspace');
|
|
308
|
+
await (0, bot_config_1.reloadConfigFromHailer)();
|
|
309
|
+
logger.info('Bot config reload completed');
|
|
310
|
+
}
|
|
311
|
+
catch (reloadError) {
|
|
312
|
+
logger.error('Failed to reload bot config', reloadError);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
catch (switchError) {
|
|
316
|
+
logger.error('Failed to switch to workspace', switchError, {
|
|
317
|
+
workspaceId: invite.cid
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
catch (acceptError) {
|
|
322
|
+
logger.error('Failed to accept invitation', acceptError, {
|
|
323
|
+
inviteKey: invite.invite_key,
|
|
324
|
+
workspaceId: invite.cid
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
// Refresh cache after accepting invitations
|
|
329
|
+
if (this.workspaceCache) {
|
|
330
|
+
const init = await this.client.socket.request('v2.core.init', [['networks']]);
|
|
331
|
+
if (init.networks) {
|
|
332
|
+
this.workspaceCache.allWorkspaces = init.networks;
|
|
333
|
+
logger.info('Workspaces cache refreshed after accepting invitations', {
|
|
334
|
+
workspaceCount: Object.keys(init.networks).length
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
logger.error('Failed to handle new invitation', error);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
196
343
|
// Public API for subscription management
|
|
197
344
|
subscribe(id, types, handler, workspaceId) {
|
|
198
345
|
// Check if subscription already exists
|
|
@@ -240,102 +387,6 @@ class SignalHandler {
|
|
|
240
387
|
this.signalHistory = [];
|
|
241
388
|
logger.info('Signal history cleared', { previousHistorySize: previousCount });
|
|
242
389
|
}
|
|
243
|
-
// Utility methods for MCP integration
|
|
244
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- MCP server type from @modelcontextprotocol/sdk
|
|
245
|
-
createMcpSignalTools(server) {
|
|
246
|
-
// Add MCP tools for signal subscription management
|
|
247
|
-
server.tool("subscribe_to_signals", "🔔 Subscribe to real-time Hailer signals - Get notified of activity updates, new messages, and workspace changes", {
|
|
248
|
-
types: zod_1.z.array(zod_1.z.enum([
|
|
249
|
-
'activities.updated',
|
|
250
|
-
'activities.created',
|
|
251
|
-
'activities.deleted',
|
|
252
|
-
'discussion.message',
|
|
253
|
-
'user.joined',
|
|
254
|
-
'user.left',
|
|
255
|
-
'workspace.updated',
|
|
256
|
-
'process.updated',
|
|
257
|
-
'cache.invalidate'
|
|
258
|
-
])).describe("Signal types to subscribe to"),
|
|
259
|
-
workspaceId: zod_1.z.string().optional().describe("Optional workspace ID to filter signals"),
|
|
260
|
-
}, async (args) => {
|
|
261
|
-
const subscriptionId = `mcp-${Date.now()}`;
|
|
262
|
-
this.subscribe(subscriptionId, args.types, (signal) => {
|
|
263
|
-
// TODO: Send MCP notification to client
|
|
264
|
-
logger.debug('MCP signal notification', {
|
|
265
|
-
subscriptionId,
|
|
266
|
-
signalType: signal.type,
|
|
267
|
-
workspaceId: signal.workspaceId
|
|
268
|
-
});
|
|
269
|
-
}, args.workspaceId);
|
|
270
|
-
return {
|
|
271
|
-
content: [{
|
|
272
|
-
type: "text",
|
|
273
|
-
text: `🔔 Subscribed to real-time signals!\n\n` +
|
|
274
|
-
`📡 Subscription ID: ${subscriptionId}\n` +
|
|
275
|
-
`📋 Signal types: ${args.types.join(", ")}\n` +
|
|
276
|
-
`${args.workspaceId ? `🏢 Workspace filter: ${args.workspaceId}\n` : ''}` +
|
|
277
|
-
`\n💡 You'll now receive real-time notifications for these events.\n` +
|
|
278
|
-
`Use get_signal_history to see recent signals.`,
|
|
279
|
-
}],
|
|
280
|
-
};
|
|
281
|
-
});
|
|
282
|
-
server.tool("get_signal_history", "📜 Get recent real-time signal history - See what's been happening in your workspace", {
|
|
283
|
-
types: zod_1.z.array(zod_1.z.string()).optional().describe("Optional filter for specific signal types"),
|
|
284
|
-
limit: zod_1.z.number().optional().default(20).describe("Maximum number of signals to return"),
|
|
285
|
-
workspaceId: zod_1.z.string().optional().describe("Optional workspace ID to filter signals"),
|
|
286
|
-
}, async (args) => {
|
|
287
|
-
const history = this.getSignalHistory(args.types, args.limit, args.workspaceId);
|
|
288
|
-
const summary = {
|
|
289
|
-
totalSignals: history.length,
|
|
290
|
-
timeRange: history.length > 0 ? {
|
|
291
|
-
newest: new Date(history[0].timestamp).toISOString(),
|
|
292
|
-
oldest: new Date(history[history.length - 1].timestamp).toISOString(),
|
|
293
|
-
} : null,
|
|
294
|
-
typeBreakdown: history.reduce((acc, signal) => {
|
|
295
|
-
acc[signal.type] = (acc[signal.type] || 0) + 1;
|
|
296
|
-
return acc;
|
|
297
|
-
}, {}),
|
|
298
|
-
signals: history.map(signal => ({
|
|
299
|
-
type: signal.type,
|
|
300
|
-
timestamp: new Date(signal.timestamp).toISOString(),
|
|
301
|
-
workspaceId: signal.workspaceId,
|
|
302
|
-
data: signal.data,
|
|
303
|
-
})),
|
|
304
|
-
};
|
|
305
|
-
return {
|
|
306
|
-
content: [{
|
|
307
|
-
type: "text",
|
|
308
|
-
text: `📜 Signal History (${history.length} signals)\n\n` +
|
|
309
|
-
`📊 Summary:\n` +
|
|
310
|
-
`${Object.entries(summary.typeBreakdown).map(([type, count]) => `- ${type}: ${count}`).join('\n')}\n\n` +
|
|
311
|
-
`⏰ Time range: ${summary.timeRange ? `${summary.timeRange.oldest} → ${summary.timeRange.newest}` : 'No signals'}\n\n` +
|
|
312
|
-
`📡 Real-time signals help you stay updated on workspace activity!\n\n` +
|
|
313
|
-
`${JSON.stringify(summary, null, 2)}`,
|
|
314
|
-
}],
|
|
315
|
-
};
|
|
316
|
-
});
|
|
317
|
-
server.tool("list_signal_subscriptions", "📋 List active signal subscriptions - See what real-time notifications are configured", {}, async () => {
|
|
318
|
-
const subscriptions = this.getActiveSubscriptions();
|
|
319
|
-
return {
|
|
320
|
-
content: [{
|
|
321
|
-
type: "text",
|
|
322
|
-
text: `📋 Active Signal Subscriptions (${subscriptions.length})\n\n` +
|
|
323
|
-
(subscriptions.length > 0
|
|
324
|
-
? subscriptions.map((sub, index) => `${index + 1}. **${sub.id}**\n` +
|
|
325
|
-
` - Types: ${sub.types.join(", ")}\n` +
|
|
326
|
-
` - Workspace: ${sub.workspaceId || "All workspaces"}`).join('\n\n')
|
|
327
|
-
: "No active subscriptions") +
|
|
328
|
-
`\n\n💡 Use subscribe_to_signals to create new subscriptions.\n` +
|
|
329
|
-
`💡 Subscriptions automatically receive real-time updates.`,
|
|
330
|
-
}],
|
|
331
|
-
};
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
390
|
}
|
|
335
391
|
exports.SignalHandler = SignalHandler;
|
|
336
|
-
// Factory function for creating signal handler
|
|
337
|
-
const createSignalHandler = (clients, workspaceCache) => {
|
|
338
|
-
return new SignalHandler(clients, workspaceCache);
|
|
339
|
-
};
|
|
340
|
-
exports.createSignalHandler = createSignalHandler;
|
|
341
392
|
//# sourceMappingURL=signal-handler.js.map
|
|
@@ -724,7 +724,15 @@ exports.createActivityTool = {
|
|
|
724
724
|
const isBulk = args.activities && Array.isArray(args.activities) && args.activities.length > 0;
|
|
725
725
|
// Get workflow's default team (owner team) if available
|
|
726
726
|
const rawWorkflow = context.init.processes.find((p) => p._id === args.workflowId);
|
|
727
|
-
|
|
727
|
+
let defaultTeamId = rawWorkflow?.team;
|
|
728
|
+
// Fallback to first workspace team if workflow has no default team
|
|
729
|
+
if (!defaultTeamId && context.init.teams) {
|
|
730
|
+
const teamIds = Object.keys(context.init.teams);
|
|
731
|
+
if (teamIds.length > 0) {
|
|
732
|
+
defaultTeamId = teamIds[0];
|
|
733
|
+
logger.debug('Using fallback team from workspace', { teamId: defaultTeamId });
|
|
734
|
+
}
|
|
735
|
+
}
|
|
728
736
|
let activitiesToCreate;
|
|
729
737
|
let options = {
|
|
730
738
|
returnDocument: true,
|
|
@@ -6,15 +6,61 @@
|
|
|
6
6
|
* - Deployed agents phase = enabled
|
|
7
7
|
* - Retired agents phase = disabled
|
|
8
8
|
*
|
|
9
|
-
* Architecture:
|
|
10
|
-
* - In-memory state is primary (for speed)
|
|
9
|
+
* Architecture (User ID-based discovery):
|
|
10
|
+
* - In-memory state is primary (for speed), keyed by USER ID
|
|
11
11
|
* - Agent Directory workflow is persistence layer (agents as activities, phases for state)
|
|
12
|
-
* - On startup: Load config from Hailer by
|
|
12
|
+
* - On startup: Load config from Hailer by extracting hailerProfile (user ID) from each agent
|
|
13
13
|
* - On change: Move agent activity between phases
|
|
14
|
+
* - Credentials: Auto-synced from Hailer to local storage on discovery
|
|
15
|
+
* - Orchestrator: Simply the first deployed bot with credentials (no special field needed)
|
|
14
16
|
*/
|
|
15
17
|
import { z } from 'zod';
|
|
16
18
|
import { Tool } from '../tool-registry';
|
|
17
19
|
import { HailerApiClient } from '../utils/index';
|
|
20
|
+
interface SchemaConfig {
|
|
21
|
+
agentDirectoryWorkflowId: string | null;
|
|
22
|
+
deployedPhaseId: string | null;
|
|
23
|
+
retiredPhaseId: string | null;
|
|
24
|
+
hailerProfileFieldId: string | null;
|
|
25
|
+
emailFieldId: string | null;
|
|
26
|
+
passwordFieldId: string | null;
|
|
27
|
+
botTypeFieldId: string | null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get workspace ID by Agent Directory workflow ID (reverse lookup)
|
|
31
|
+
*/
|
|
32
|
+
export declare function getWorkspaceByWorkflowId(workflowId: string): string | null;
|
|
33
|
+
/**
|
|
34
|
+
* Get all configured workspace IDs
|
|
35
|
+
*/
|
|
36
|
+
export declare function getAllConfiguredWorkspaces(): string[];
|
|
37
|
+
/**
|
|
38
|
+
* Set the active workspace ID (called when switching workspaces)
|
|
39
|
+
*/
|
|
40
|
+
export declare function setActiveWorkspace(workspaceId: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* Get the active workspace ID
|
|
43
|
+
*/
|
|
44
|
+
export declare function getActiveWorkspace(): string | null;
|
|
45
|
+
/**
|
|
46
|
+
* Get schema for a workspace
|
|
47
|
+
*/
|
|
48
|
+
export declare function getWorkspaceSchema(workspaceId: string): SchemaConfig | null;
|
|
49
|
+
/**
|
|
50
|
+
* Set schema for a workspace
|
|
51
|
+
*/
|
|
52
|
+
export declare function setWorkspaceSchema(workspaceId: string, schema: SchemaConfig): void;
|
|
53
|
+
/**
|
|
54
|
+
* Get discovered workflow ID for a workspace (or active workspace if not specified)
|
|
55
|
+
*/
|
|
56
|
+
export declare function getAgentDirectoryWorkflowId(workspaceId?: string): string | null;
|
|
57
|
+
/**
|
|
58
|
+
* Get discovered phase IDs for a workspace (or active workspace if not specified)
|
|
59
|
+
*/
|
|
60
|
+
export declare function getPhaseIds(workspaceId?: string): {
|
|
61
|
+
deployed: string | null;
|
|
62
|
+
retired: string | null;
|
|
63
|
+
};
|
|
18
64
|
/**
|
|
19
65
|
* Available bots - single source of truth
|
|
20
66
|
*
|
|
@@ -27,8 +73,43 @@ export declare const AVAILABLE_BOTS: {
|
|
|
27
73
|
description: string;
|
|
28
74
|
icon: string;
|
|
29
75
|
}[];
|
|
76
|
+
/**
|
|
77
|
+
* Get the orchestrator user ID
|
|
78
|
+
* Returns the user ID of the bot marked as orchestrator in Agent Directory
|
|
79
|
+
*/
|
|
80
|
+
export declare function getOrchestratorUserId(): string | null;
|
|
81
|
+
/**
|
|
82
|
+
* Set the orchestrator user ID
|
|
83
|
+
* Called during Agent Directory sync - set to first deployed bot with credentials
|
|
84
|
+
*/
|
|
85
|
+
export declare function setOrchestratorUserId(userId: string | null): void;
|
|
30
86
|
export declare function onBotStateChange(callback: (botId: string, enabled: boolean) => void): void;
|
|
87
|
+
export declare function onBotCredentialsChange(callback: (botId: string) => void): void;
|
|
88
|
+
export declare function onDaemonRestartNeeded(callback: () => void): void;
|
|
89
|
+
/**
|
|
90
|
+
* Cleanup function to clear pending timers and state on shutdown
|
|
91
|
+
* Should be called from Core.stop() to prevent orphaned callbacks
|
|
92
|
+
*/
|
|
93
|
+
export declare function cleanupBotConfig(): void;
|
|
31
94
|
export declare function getBotState(): Record<string, boolean>;
|
|
95
|
+
/**
|
|
96
|
+
* Get the user ID associated with a bot
|
|
97
|
+
*/
|
|
98
|
+
export declare function getBotUserId(botId: string): string | null;
|
|
99
|
+
/**
|
|
100
|
+
* Get the bot type for a user ID
|
|
101
|
+
* Returns values like 'orchestrator', 'workflow-expert', 'bug-fixer', etc.
|
|
102
|
+
*/
|
|
103
|
+
export declare function getBotType(userId: string): string | null;
|
|
104
|
+
/**
|
|
105
|
+
* Get all enabled bots with their types
|
|
106
|
+
* Returns map of userId -> botType for enabled bots only
|
|
107
|
+
*/
|
|
108
|
+
export declare function getEnabledBotsWithTypes(): Map<string, string | null>;
|
|
109
|
+
/**
|
|
110
|
+
* Set the user ID associated with a bot
|
|
111
|
+
*/
|
|
112
|
+
export declare function setBotUserId(botId: string, userId: string | null): void;
|
|
32
113
|
/**
|
|
33
114
|
* Set bot enabled state
|
|
34
115
|
*
|
|
@@ -36,29 +117,117 @@ export declare function getBotState(): Record<string, boolean>;
|
|
|
36
117
|
* the agent activity to the appropriate phase (deployed/retired).
|
|
37
118
|
*/
|
|
38
119
|
export declare function setBotEnabled(botId: string, enabled: boolean): void;
|
|
120
|
+
/**
|
|
121
|
+
* Handle activity phase change signal from Hailer
|
|
122
|
+
*
|
|
123
|
+
* Called by signal-handler when an activity.updated signal is received.
|
|
124
|
+
* Updates bot state if the activity belongs to Agent Directory workflow.
|
|
125
|
+
*
|
|
126
|
+
* NEW: Now looks up activities by user ID instead of bot type.
|
|
127
|
+
*
|
|
128
|
+
* @param processId - Workflow ID from the signal
|
|
129
|
+
* @param activityIds - Array of activity IDs that were updated
|
|
130
|
+
* @param newPhaseId - The phase the activities moved to
|
|
131
|
+
*/
|
|
132
|
+
export declare function handleActivityPhaseChange(processId: string, activityIds: string[], newPhaseId: string): Promise<void>;
|
|
133
|
+
/**
|
|
134
|
+
* Force reload bot config from Hailer
|
|
135
|
+
*
|
|
136
|
+
* Can be called to manually refresh state from Hailer.
|
|
137
|
+
* NEW: Now works with user IDs instead of bot IDs.
|
|
138
|
+
*
|
|
139
|
+
* Also handles hot-reload: if schema discovery hasn't succeeded yet
|
|
140
|
+
* (e.g., AI Hub template wasn't installed at startup), it will
|
|
141
|
+
* retry discovery and initialize the daemon.
|
|
142
|
+
*/
|
|
143
|
+
export declare function reloadConfigFromHailer(): Promise<void>;
|
|
39
144
|
/**
|
|
40
145
|
* Initialize persistence layer
|
|
41
146
|
* Called once during MCP server startup
|
|
42
147
|
*
|
|
43
|
-
* Loads
|
|
44
|
-
* -
|
|
45
|
-
* - Agents in RETIRED_PHASE_ID are disabled
|
|
148
|
+
* Loads bot config from local files only (no Hailer API calls on startup).
|
|
149
|
+
* Real-time updates from Hailer are handled via signal handler when activities change.
|
|
46
150
|
*
|
|
47
151
|
* @param client - HailerApiClient instance for API calls
|
|
48
152
|
*/
|
|
153
|
+
/**
|
|
154
|
+
* Set the target workspace ID for bot config operations
|
|
155
|
+
* Called by signal handler when workspace is switched
|
|
156
|
+
*/
|
|
49
157
|
export declare function initBotConfigPersistence(client: HailerApiClient): Promise<void>;
|
|
158
|
+
/**
|
|
159
|
+
* Initialize workspace schema for a specific workspace
|
|
160
|
+
* Called by bot clients after connecting to discover the Agent Directory in that workspace
|
|
161
|
+
* @param client - HailerApiClient for API calls
|
|
162
|
+
* @param workspaceId - The workspace ID to initialize schema for
|
|
163
|
+
*/
|
|
164
|
+
export declare function initWorkspaceSchema(client: HailerApiClient, workspaceId: string): Promise<boolean>;
|
|
50
165
|
/**
|
|
51
166
|
* Check if persistence is initialized
|
|
52
167
|
*/
|
|
53
168
|
export declare function isPersistenceInitialized(): boolean;
|
|
169
|
+
/**
|
|
170
|
+
* Bot credentials interface
|
|
171
|
+
*/
|
|
172
|
+
export interface BotCredentials {
|
|
173
|
+
email?: string;
|
|
174
|
+
password?: string;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get bot credentials from Agent Directory
|
|
178
|
+
*
|
|
179
|
+
* Reads the email and password fields from the bot's activity.
|
|
180
|
+
* Password is masked for security unless showPassword is true.
|
|
181
|
+
* Falls back to local file storage if Hailer is unavailable.
|
|
182
|
+
*
|
|
183
|
+
* @param botIdOrActivityId - The bot ID (hal, giuseppe, etc.) or activity ID
|
|
184
|
+
* @param showPassword - If true, returns unmasked password. Default false for security.
|
|
185
|
+
* @returns Credentials or null if not found
|
|
186
|
+
*/
|
|
187
|
+
export declare function getBotCredentials(botIdOrActivityId: string, showPassword?: boolean): Promise<BotCredentials | null>;
|
|
188
|
+
/**
|
|
189
|
+
* Update bot credentials in Agent Directory
|
|
190
|
+
*
|
|
191
|
+
* Writes the email and/or password to the bot's activity fields.
|
|
192
|
+
* Only updates fields that are provided.
|
|
193
|
+
* Saves to local file storage as backup (and uses as fallback if Hailer unavailable).
|
|
194
|
+
*
|
|
195
|
+
* @param botIdOrActivityId - The bot ID (hal, giuseppe, etc.) or raw activity ID
|
|
196
|
+
* @param credentials - Credentials to update (email and/or password)
|
|
197
|
+
*/
|
|
198
|
+
export declare function updateBotCredentials(botIdOrActivityId: string, credentials: BotCredentials): Promise<void>;
|
|
199
|
+
/**
|
|
200
|
+
* Get unmasked bot credentials for daemon creation
|
|
201
|
+
*
|
|
202
|
+
* NEW: Now primarily looks up by user ID instead of bot ID.
|
|
203
|
+
* This function returns the actual password (not masked) from local storage.
|
|
204
|
+
* Should only be used internally for creating bot daemon instances.
|
|
205
|
+
*
|
|
206
|
+
* @param userIdOrBotId - The user ID or legacy bot ID (hal, giuseppe, etc.)
|
|
207
|
+
* @returns Unmasked credentials or null if not found
|
|
208
|
+
*/
|
|
209
|
+
export declare function getLocalBotCredentials(userIdOrBotId: string): {
|
|
210
|
+
email: string;
|
|
211
|
+
password: string;
|
|
212
|
+
displayName?: string;
|
|
213
|
+
} | null;
|
|
214
|
+
/**
|
|
215
|
+
* Get all local bot credentials (for daemon initialization)
|
|
216
|
+
* Returns all bots stored in .bot-credentials.json
|
|
217
|
+
*/
|
|
218
|
+
export declare function getAllLocalBotCredentials(): Record<string, {
|
|
219
|
+
email: string;
|
|
220
|
+
password: string;
|
|
221
|
+
displayName?: string;
|
|
222
|
+
}>;
|
|
54
223
|
/**
|
|
55
224
|
* Get persistence status info (for debugging)
|
|
56
225
|
*/
|
|
57
226
|
export declare function getPersistenceStatus(): {
|
|
58
227
|
initialized: boolean;
|
|
59
|
-
workflowId: string;
|
|
60
|
-
deployedPhaseId: string;
|
|
61
|
-
retiredPhaseId: string;
|
|
228
|
+
workflowId: string | null;
|
|
229
|
+
deployedPhaseId: string | null;
|
|
230
|
+
retiredPhaseId: string | null;
|
|
62
231
|
agentActivityIds: Record<string, string>;
|
|
63
232
|
hasClient: boolean;
|
|
64
233
|
};
|
|
@@ -74,5 +243,11 @@ export declare const enableBotTool: Tool;
|
|
|
74
243
|
* Disable a bot
|
|
75
244
|
*/
|
|
76
245
|
export declare const disableBotTool: Tool;
|
|
246
|
+
/**
|
|
247
|
+
* Check specialist status from Agent Directory
|
|
248
|
+
* Returns real-time status showing which specialists are deployed vs retired
|
|
249
|
+
*/
|
|
250
|
+
export declare const checkSpecialistStatusTool: Tool;
|
|
77
251
|
export declare const botConfigTools: Tool<z.ZodType<any, z.ZodTypeDef, any>>[];
|
|
252
|
+
export {};
|
|
78
253
|
//# sourceMappingURL=bot-config.d.ts.map
|