@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
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createSignalHandler = exports.SignalHandler = void 0;
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
- // Register for all signals from the socket client
20
- this.client.socket.on('signals', (signal) => {
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 raw signal data to discover available fields
41
- if (eventType === 'messenger.new') {
42
- const data = eventData;
43
- logger.debug('Raw messenger.new signal data', {
44
- eventType,
45
- dataKeys: Object.keys(eventData),
46
- sid: String(data.sid || ''),
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?._id || signal.data?.id,
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
- // TODO: Update workspace cache if we have the activity cached
121
- // This could invalidate cached activity lists and trigger re-fetching
122
- // Example: Emit MCP notification for real-time updates
123
- // this.emitMcpNotification('activity_updated', signal.data);
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: signal.data?._id || signal.data?.id,
128
- workspaceId: signal.workspaceId
182
+ activityId: Array.isArray(activityIdRaw) ? activityIdRaw.join(',') : activityIdRaw,
183
+ workspaceId: signal.workspaceId,
184
+ processId,
185
+ phase,
129
186
  });
130
- // TODO: Add new activity to workspace cache
131
- // This could update activity counts and lists
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
- const init = await this.client.socket.request('v2.core.init', [['users', 'network', 'networks']]);
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
- const defaultTeamId = rawWorkflow?.team;
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 checking which phase each agent is in
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 agent enabled state from the Agent Directory workflow:
44
- * - Agents in DEPLOYED_PHASE_ID are enabled
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