@hailer/mcp 0.1.6 โ†’ 0.1.9

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 (137) hide show
  1. package/.claude/agents/agent-dmitri-activity-crud.md +3 -1
  2. package/.claude/agents/agent-giuseppe-app-builder.md +11 -12
  3. package/.claude/agents/agent-kenji-data-reader.md +5 -3
  4. package/.claude/hooks/sync-marketplace-agents.cjs +117 -56
  5. package/.claude/skills/hailer-app-builder/SKILL.md +506 -0
  6. package/.claude/skills/publish-hailer-app/SKILL.md +169 -0
  7. package/.claude/skills/tool-parameter-usage/SKILL.md +112 -0
  8. package/CHANGELOG.md +20 -0
  9. package/CLAUDE.md +37 -16
  10. package/REFACTOR_STATUS.md +127 -0
  11. package/dist/cli.js +0 -0
  12. package/dist/client/agents/base.d.ts +202 -0
  13. package/dist/client/agents/base.js +737 -0
  14. package/dist/client/agents/definitions.d.ts +53 -0
  15. package/dist/client/agents/definitions.js +178 -0
  16. package/dist/client/agents/orchestrator.d.ts +119 -0
  17. package/dist/client/agents/orchestrator.js +760 -0
  18. package/dist/client/agents/specialist.d.ts +86 -0
  19. package/dist/client/agents/specialist.js +340 -0
  20. package/dist/client/bot-manager.d.ts +44 -0
  21. package/dist/client/bot-manager.js +173 -0
  22. package/dist/client/chat-agent-daemon.d.ts +464 -0
  23. package/dist/client/chat-agent-daemon.js +1774 -0
  24. package/dist/client/daemon-factory.d.ts +106 -0
  25. package/dist/client/daemon-factory.js +301 -0
  26. package/dist/client/factory.d.ts +107 -0
  27. package/dist/client/factory.js +304 -0
  28. package/dist/client/index.d.ts +17 -0
  29. package/dist/client/index.js +38 -0
  30. package/dist/client/multi-bot-manager.d.ts +18 -0
  31. package/dist/client/multi-bot-manager.js +88 -1
  32. package/dist/client/orchestrator-daemon.d.ts +87 -0
  33. package/dist/client/orchestrator-daemon.js +444 -0
  34. package/dist/client/services/agent-registry.d.ts +108 -0
  35. package/dist/client/services/agent-registry.js +630 -0
  36. package/dist/client/services/conversation-manager.d.ts +50 -0
  37. package/dist/client/services/conversation-manager.js +136 -0
  38. package/dist/client/services/mcp-client.d.ts +48 -0
  39. package/dist/client/services/mcp-client.js +105 -0
  40. package/dist/client/services/message-classifier.d.ts +37 -0
  41. package/dist/client/services/message-classifier.js +187 -0
  42. package/dist/client/services/message-formatter.d.ts +84 -0
  43. package/dist/client/services/message-formatter.js +353 -0
  44. package/dist/client/services/session-logger.d.ts +106 -0
  45. package/dist/client/services/session-logger.js +446 -0
  46. package/dist/client/services/tool-executor.d.ts +41 -0
  47. package/dist/client/services/tool-executor.js +169 -0
  48. package/dist/client/services/workspace-schema-cache.d.ts +149 -0
  49. package/dist/client/services/workspace-schema-cache.js +732 -0
  50. package/dist/client/specialist-daemon.d.ts +77 -0
  51. package/dist/client/specialist-daemon.js +197 -0
  52. package/dist/client/specialists.d.ts +53 -0
  53. package/dist/client/specialists.js +178 -0
  54. package/dist/client/tool-schema-loader.d.ts +4 -3
  55. package/dist/client/tool-schema-loader.js +54 -8
  56. package/dist/client/types.d.ts +283 -55
  57. package/dist/client/types.js +113 -2
  58. package/dist/config.d.ts +1 -1
  59. package/dist/config.js +1 -1
  60. package/dist/core.d.ts +10 -2
  61. package/dist/core.js +43 -27
  62. package/dist/lib/logger.js +15 -3
  63. package/dist/mcp/UserContextCache.js +2 -2
  64. package/dist/mcp/hailer-clients.js +5 -5
  65. package/dist/mcp/signal-handler.js +27 -5
  66. package/dist/mcp/tools/activity.js +137 -65
  67. package/dist/mcp/tools/app-core.js +4 -140
  68. package/dist/mcp/tools/app-marketplace.js +15 -260
  69. package/dist/mcp/tools/app-member.js +2 -73
  70. package/dist/mcp/tools/app-scaffold.js +146 -87
  71. package/dist/mcp/tools/discussion.js +348 -73
  72. package/dist/mcp/tools/insight.js +74 -190
  73. package/dist/mcp/tools/workflow.js +20 -94
  74. package/dist/mcp/utils/hailer-api-client.d.ts +4 -2
  75. package/dist/mcp/utils/hailer-api-client.js +24 -10
  76. package/dist/mcp-server.d.ts +4 -0
  77. package/dist/mcp-server.js +24 -4
  78. package/dist/routes/agents.d.ts +44 -0
  79. package/dist/routes/agents.js +311 -0
  80. package/dist/services/agent-credential-store.d.ts +73 -0
  81. package/dist/services/agent-credential-store.js +212 -0
  82. package/lineup-manager/dist/assets/index-8ce6041d.css +1 -0
  83. package/lineup-manager/dist/assets/index-e168f265.js +600 -0
  84. package/lineup-manager/dist/index.html +15 -0
  85. package/lineup-manager/dist/manifest.json +17 -0
  86. package/lineup-manager/dist/vite.svg +1 -0
  87. package/package.json +1 -1
  88. package/dist/client/adaptive-documentation-bot.d.ts +0 -106
  89. package/dist/client/adaptive-documentation-bot.js +0 -464
  90. package/dist/client/adaptive-documentation-types.d.ts +0 -66
  91. package/dist/client/adaptive-documentation-types.js +0 -9
  92. package/dist/client/agent-activity-bot.d.ts +0 -51
  93. package/dist/client/agent-activity-bot.js +0 -166
  94. package/dist/client/agent-tracker.d.ts +0 -499
  95. package/dist/client/agent-tracker.js +0 -659
  96. package/dist/client/description-updater.d.ts +0 -56
  97. package/dist/client/description-updater.js +0 -259
  98. package/dist/client/log-parser.d.ts +0 -72
  99. package/dist/client/log-parser.js +0 -387
  100. package/dist/client/mcp-assistant.d.ts +0 -21
  101. package/dist/client/mcp-assistant.js +0 -58
  102. package/dist/client/mcp-client.d.ts +0 -50
  103. package/dist/client/mcp-client.js +0 -538
  104. package/dist/client/message-processor.d.ts +0 -35
  105. package/dist/client/message-processor.js +0 -357
  106. package/dist/client/providers/anthropic-provider.d.ts +0 -19
  107. package/dist/client/providers/anthropic-provider.js +0 -645
  108. package/dist/client/providers/assistant-provider.d.ts +0 -17
  109. package/dist/client/providers/assistant-provider.js +0 -51
  110. package/dist/client/providers/llm-provider.d.ts +0 -47
  111. package/dist/client/providers/llm-provider.js +0 -367
  112. package/dist/client/providers/openai-provider.d.ts +0 -23
  113. package/dist/client/providers/openai-provider.js +0 -630
  114. package/dist/client/simple-llm-caller.d.ts +0 -19
  115. package/dist/client/simple-llm-caller.js +0 -100
  116. package/dist/client/skill-generator.d.ts +0 -81
  117. package/dist/client/skill-generator.js +0 -386
  118. package/dist/client/test-adaptive-bot.d.ts +0 -9
  119. package/dist/client/test-adaptive-bot.js +0 -82
  120. package/dist/client/token-pricing.d.ts +0 -38
  121. package/dist/client/token-pricing.js +0 -127
  122. package/dist/client/token-tracker.d.ts +0 -232
  123. package/dist/client/token-tracker.js +0 -457
  124. package/dist/client/token-usage-bot.d.ts +0 -53
  125. package/dist/client/token-usage-bot.js +0 -153
  126. package/dist/client/tool-executor.d.ts +0 -69
  127. package/dist/client/tool-executor.js +0 -159
  128. package/dist/lib/materialize.d.ts +0 -3
  129. package/dist/lib/materialize.js +0 -101
  130. package/dist/lib/normalizedName.d.ts +0 -7
  131. package/dist/lib/normalizedName.js +0 -48
  132. package/dist/lib/terminal-prompt.d.ts +0 -9
  133. package/dist/lib/terminal-prompt.js +0 -108
  134. package/dist/mcp/tools/skill.d.ts +0 -10
  135. package/dist/mcp/tools/skill.js +0 -279
  136. package/dist/mcp/tools/workflow-template.d.ts +0 -19
  137. package/dist/mcp/tools/workflow-template.js +0 -822
@@ -21,31 +21,65 @@ const logger = (0, logger_1.createLogger)({ component: 'insight-tools' });
21
21
  // ============================================================================
22
22
  /**
23
23
  * Field definition schema - either meta field or custom fieldId
24
+ * Accepts both 'id' and 'fieldId' for flexibility (LLMs often use 'id')
24
25
  */
25
26
  const fieldSchema = zod_1.z.object({
26
27
  name: zod_1.z.string().describe("Column name in SQL query"),
27
- }).and(zod_1.z.union([
28
- zod_1.z.object({
29
- meta: zod_1.z.enum([
30
- '_id',
31
- 'uid',
32
- 'team',
33
- 'createdBy',
34
- 'name',
35
- 'created',
36
- 'updated',
37
- 'phaseId',
38
- 'phaseName',
39
- 'phaseLastMove',
40
- 'workflowId',
41
- 'workflowName',
42
- 'priority'
43
- ]).describe("Built-in activity property"),
44
- }),
45
- zod_1.z.object({
46
- fieldId: zod_1.z.string().describe("Custom field ID from workflow"),
47
- }),
48
- ]));
28
+ // Accept both 'id' and 'fieldId' - will be normalized to 'fieldId'
29
+ id: zod_1.z.string().optional().describe("Field ID (alias for fieldId)"),
30
+ fieldId: zod_1.z.string().optional().describe("Custom field ID from workflow"),
31
+ meta: zod_1.z.enum([
32
+ '_id',
33
+ 'uid',
34
+ 'team',
35
+ 'createdBy',
36
+ 'name',
37
+ 'created',
38
+ 'updated',
39
+ 'phaseId',
40
+ 'phaseName',
41
+ 'phaseLastMove',
42
+ 'workflowId',
43
+ 'workflowName',
44
+ 'priority'
45
+ ]).optional().describe("Built-in activity property (alternative to fieldId)"),
46
+ }).refine((data) => data.meta || data.fieldId || data.id, { message: "Field must have either 'meta', 'fieldId', or 'id'" });
47
+ /**
48
+ * Sanitize field name to be SQL-identifier safe (no spaces, special chars)
49
+ */
50
+ function sanitizeFieldName(name) {
51
+ // Convert to camelCase: "Injury Type" -> "InjuryType", "Expected Recovery" -> "ExpectedRecovery"
52
+ return name
53
+ .split(/\s+/)
54
+ .map((word, index) => {
55
+ if (index === 0)
56
+ return word.charAt(0).toLowerCase() + word.slice(1);
57
+ return word.charAt(0).toUpperCase() + word.slice(1);
58
+ })
59
+ .join('')
60
+ .replace(/[^a-zA-Z0-9_]/g, ''); // Remove any remaining special chars
61
+ }
62
+ /**
63
+ * Normalize field definitions: convert 'id' to 'fieldId' and sanitize names
64
+ */
65
+ function normalizeFields(sources) {
66
+ return sources.map(source => ({
67
+ ...source,
68
+ fields: source.fields.map(field => {
69
+ let normalized = { ...field };
70
+ // Sanitize field name (remove spaces, make SQL-safe)
71
+ if (normalized.name) {
72
+ normalized.name = sanitizeFieldName(normalized.name);
73
+ }
74
+ // If 'id' is provided but not 'fieldId', use 'id' as 'fieldId'
75
+ if (field.id && !field.fieldId && !field.meta) {
76
+ const { id, ...rest } = normalized;
77
+ return { ...rest, fieldId: id };
78
+ }
79
+ return normalized;
80
+ }),
81
+ }));
82
+ }
49
83
  /**
50
84
  * Source definition schema - workflow as SQL table
51
85
  */
@@ -57,22 +91,7 @@ const sourceSchema = zod_1.z.object({
57
91
  // ============================================================================
58
92
  // CREATE INSIGHT TOOL
59
93
  // ============================================================================
60
- const createInsightDescription = `๐Ÿงช [PLAYGROUND] Create SQL-like insights (workflows = tables, activities = rows, fields = columns)
61
-
62
- โš ๏ธ **MANDATORY: YOU MUST LOAD SKILL FIRST**
63
- **BEFORE calling this tool, you are REQUIRED to:**
64
- 1. Load the skill: \`get_skill({ skillName: "create-insight-skill" })\`
65
- 2. Read the query examples, JOIN patterns, and aggregation examples
66
- 3. Test your SQL with \`preview_insight\` first
67
- **FAILURE TO LOAD THE SKILL FIRST IS AN ERROR**
68
-
69
- **Required Parameters**:
70
- - name: Insight name
71
- - sources: Array with name (table alias), workflowId, fields (meta or fieldId)
72
- - query: Standard SQL (SELECT, JOIN, WHERE, GROUP BY, ORDER BY, LIMIT)
73
-
74
- **Meta Fields**: _id, uid, team, createdBy, name, created, updated, phaseId, phaseName, phaseLastMove, workflowId, workflowName, priority
75
- **SQL Support**: All standard SQL features including JOINs and aggregations`;
94
+ const createInsightDescription = `Create SQL insight over workflow data (workflows=tables, activities=rows, fields=columns)`;
76
95
  exports.createInsightTool = {
77
96
  name: 'create_insight',
78
97
  group: tool_registry_1.ToolGroup.PLAYGROUND,
@@ -118,11 +137,13 @@ exports.createInsightTool = {
118
137
  name: args.name,
119
138
  sourcesCount: args.sources.length
120
139
  });
140
+ // Normalize sources (convert 'id' to 'fieldId' if needed)
141
+ const normalizedSources = normalizeFields(args.sources);
121
142
  // Build insight payload
122
143
  const insightData = {
123
144
  name: args.name,
124
145
  public: args.public || false,
125
- sources: args.sources,
146
+ sources: normalizedSources,
126
147
  query: args.query,
127
148
  };
128
149
  // Call v3.insight.create endpoint
@@ -233,21 +254,7 @@ exports.createInsightTool = {
233
254
  // ============================================================================
234
255
  // PREVIEW INSIGHT TOOL
235
256
  // ============================================================================
236
- const previewInsightDescription = `๐Ÿงช [PLAYGROUND] Preview insight - Test SQL on ~20 activities before saving
237
-
238
- โš ๏ธ **MANDATORY: YOU MUST LOAD SKILL FIRST**
239
- **BEFORE calling this tool, you are REQUIRED to:**
240
- 1. Load the skill: \`get_skill({ skillName: "preview-insight-skill" })\`
241
- 2. Read the debugging tips and security notes
242
- 3. Understand the SQL testing patterns
243
- **FAILURE TO LOAD THE SKILL FIRST IS AN ERROR**
244
-
245
- **Purpose**: Debug SQL syntax, test JOINs, verify field mappings without creating insight
246
-
247
- **Required Parameters**:
248
- - sources: Same format as create_insight (name, workflowId, fields)
249
- - query: SQL query to test
250
- - insightId (optional): Test changes to existing insight`;
257
+ const previewInsightDescription = `Test SQL query on sample data before creating/updating insight`;
251
258
  exports.previewInsightTool = {
252
259
  name: 'preview_insight',
253
260
  group: tool_registry_1.ToolGroup.PLAYGROUND,
@@ -288,9 +295,11 @@ exports.previewInsightTool = {
288
295
  sourcesCount: args.sources.length,
289
296
  insightId: args.insightId
290
297
  });
298
+ // Normalize sources (convert 'id' to 'fieldId' if needed)
299
+ const normalizedSources = normalizeFields(args.sources);
291
300
  // Build preview payload
292
301
  const previewData = {
293
- sources: args.sources,
302
+ sources: normalizedSources,
294
303
  query: args.query,
295
304
  ...(args.insightId && { insightId: args.insightId }),
296
305
  };
@@ -362,33 +371,7 @@ exports.previewInsightTool = {
362
371
  // ============================================================================
363
372
  // GET INSIGHT DATA TOOL
364
373
  // ============================================================================
365
- const getInsightDataDescription = `๐Ÿงช [PLAYGROUND] Get Insight Data - Execute insight and retrieve results
366
-
367
- **What is Get Insight Data?**
368
- Executes a saved insight's SQL query and returns the complete results. Unlike preview (limited to ~20 rows), this returns all matching activities.
369
-
370
- **When to Use**:
371
- - Get full results from a saved insight
372
- - Export insight data
373
- - Verify insight after creation
374
- - Check updated data after changes
375
-
376
- **Example - Basic Usage**:
377
- \`\`\`javascript
378
- get_insight_data({
379
- insightId: '<insight-id>'
380
- })
381
- \`\`\`
382
-
383
- **Update Option**:
384
- - \`update: false\` (default) - Returns cached results (faster)
385
- - \`update: true\` - Recalculates from current activity data (slower but fresh)
386
-
387
- **Tips**:
388
- - Use \`update: true\` if activity data has changed
389
- - Results can be large - may take time for big datasets
390
- - Use \`preview_insight\` first to test queries
391
- - Get insight ID from \`create_insight\` or \`list_insights\``;
374
+ const getInsightDataDescription = `Execute insight and get results`;
392
375
  exports.getInsightDataTool = {
393
376
  name: 'get_insight_data',
394
377
  group: tool_registry_1.ToolGroup.PLAYGROUND,
@@ -484,51 +467,7 @@ exports.getInsightDataTool = {
484
467
  // ============================================================================
485
468
  // UPDATE INSIGHT TOOL
486
469
  // ============================================================================
487
- const updateInsightDescription = `๐Ÿงช [PLAYGROUND] Update Insight - Modify existing insight properties
488
-
489
- **What it does:**
490
- Updates an existing insight's properties (name, query, sources, visibility).
491
-
492
- **Required Parameters:**
493
- - insightId - The insight ID to update
494
-
495
- **Optional Parameters (include only what you want to change):**
496
- - name - New insight name
497
- - public - Change visibility (true/false)
498
- - sources - Updated workflow sources
499
- - query - Updated SQL query
500
-
501
- **Example - Update name only:**
502
- \`\`\`javascript
503
- update_insight({
504
- insightId: '<insight-id>',
505
- name: 'New Insight Name'
506
- })
507
- \`\`\`
508
-
509
- **Example - Update query:**
510
- \`\`\`javascript
511
- update_insight({
512
- insightId: '<insight-id>',
513
- query: 'SELECT * FROM players WHERE position = "Forward"'
514
- })
515
- \`\`\`
516
-
517
- **Example - Full update:**
518
- \`\`\`javascript
519
- update_insight({
520
- insightId: '<insight-id>',
521
- name: 'Updated Insight',
522
- public: true,
523
- sources: [{ name: 'p', workflowId: 'xxx', fields: [...] }],
524
- query: 'SELECT * FROM p'
525
- })
526
- \`\`\`
527
-
528
- **Tips:**
529
- - Use \`preview_insight\` to test query changes before updating
530
- - Use \`list_insights\` to find insight IDs
531
- - Only include fields you want to change`;
470
+ const updateInsightDescription = `Update existing insight (name, query, sources, or visibility)`;
532
471
  exports.updateInsightTool = {
533
472
  name: 'update_insight',
534
473
  group: tool_registry_1.ToolGroup.PLAYGROUND,
@@ -572,7 +511,7 @@ exports.updateInsightTool = {
572
511
  updateData.public = args.public;
573
512
  }
574
513
  if (args.sources !== undefined) {
575
- updateData.sources = args.sources;
514
+ updateData.sources = normalizeFields(args.sources);
576
515
  }
577
516
  if (args.query !== undefined) {
578
517
  updateData.query = args.query;
@@ -672,46 +611,7 @@ exports.updateInsightTool = {
672
611
  // ============================================================================
673
612
  // REMOVE INSIGHT TOOL
674
613
  // ============================================================================
675
- const removeInsightDescription = `๐Ÿงช [PLAYGROUND] Remove Insight - โš ๏ธ DANGER: Permanently deletes insight and all its data
676
-
677
- โš ๏ธ **MANDATORY: GATHER COMPLETE CONTEXT BEFORE CALLING THIS TOOL**
678
- **BEFORE calling this tool, you are REQUIRED to:**
679
- 1. Load the skill: \`get_skill({ skillName: "remove-insight-skill" })\`
680
- 2. Fetch insight details with \`list_insights\` to get:
681
- - Insight name and ID
682
- - Workspace ID and name
683
- - SQL query
684
- - Source workflows
685
- - Number of sources
686
- 3. Show comprehensive confirmation message including:
687
- - Workspace ID and name
688
- - Insight ID and name
689
- - What will be deleted (definition, results, permissions, discussion)
690
- - Sources being queried (workflows)
691
- - Clear irreversibility warning
692
- 4. Wait for explicit user confirmation
693
- **FAILURE TO GATHER AND SHOW THIS CONTEXT IS AN ERROR**
694
-
695
- **Required**: insightId
696
- **Permission**: Workspace administrator
697
-
698
- **What Gets Deleted**:
699
- - Insight definition (sources, query, name)
700
- - Saved query results
701
- - Insight permissions
702
- - Associated discussion
703
-
704
- **Example**:
705
- \`\`\`javascript
706
- remove_insight({
707
- insightId: '<insight-id>'
708
- })
709
- \`\`\`
710
-
711
- **Tips**:
712
- - Use \`list_insights\` to find insight IDs
713
- - Use \`get_insight_data\` to verify before deleting
714
- - Operation cannot be undone - data is permanently lost`;
614
+ const removeInsightDescription = `Delete insight permanently`;
715
615
  exports.removeInsightTool = {
716
616
  name: 'remove_insight',
717
617
  group: tool_registry_1.ToolGroup.NUCLEAR,
@@ -839,26 +739,7 @@ exports.removeInsightTool = {
839
739
  // ============================================================================
840
740
  // LIST INSIGHTS TOOL
841
741
  // ============================================================================
842
- const listInsightsDescription = `๐Ÿงช [PLAYGROUND] List Insights - View all insights in workspace
843
-
844
- **What are Insights?**
845
- Insights are SQL-like reports over Hailer workflow data. They allow querying activities like database tables.
846
-
847
- **Purpose**: List all insights in the workspace to find insight IDs.
848
-
849
- **Example**:
850
- \`\`\`javascript
851
- list_insights({
852
- workspaceId: "optional-workspace-id"
853
- })
854
- \`\`\`
855
-
856
- **Response includes**:
857
- - Insight ID and name
858
- - SQL query
859
- - Sources (workflows being queried)
860
- - Public/private status
861
- - Created/updated timestamps`;
742
+ const listInsightsDescription = `List all insights in workspace`;
862
743
  exports.listInsightsTool = {
863
744
  name: 'list_insights',
864
745
  group: tool_registry_1.ToolGroup.PLAYGROUND,
@@ -901,8 +782,11 @@ exports.listInsightsTool = {
901
782
  responseText += `${index + 1}. **${insight.name}**\n`;
902
783
  responseText += ` - Insight ID: \`${insight._id}\`\n`;
903
784
  responseText += ` - Public: ${insight.public ? 'Yes' : 'No'}\n`;
904
- responseText += ` - Sources: ${insight.sources.length} workflow(s)\n`;
905
- responseText += ` - Query: \`${insight.query.substring(0, 80)}${insight.query.length > 80 ? '...' : ''}\`\n\n`;
785
+ // Show source names (table aliases) - critical for SQL debugging
786
+ const sourceNames = insight.sources?.map((s) => s.name).join(', ') || 'none';
787
+ responseText += ` - Table names: \`${sourceNames}\` (use EXACTLY these in SQL FROM/JOIN)\n`;
788
+ responseText += ` - Sources: ${insight.sources?.length || 0} workflow(s)\n`;
789
+ responseText += ` - Query: \`${insight.query?.substring(0, 80)}${(insight.query?.length || 0) > 80 ? '...' : ''}\`\n\n`;
906
790
  });
907
791
  responseText += `๐Ÿ’ก **Next Steps:**\n`;
908
792
  responseText += `- Use \`get_insight_data\` to execute an insight\n`;
@@ -60,6 +60,7 @@ exports.getWorkflowSchemaTool = {
60
60
  schema: zod_1.z.object({
61
61
  workflowId: zod_1.z.string().describe("Workflow ID to get schema from"),
62
62
  phaseId: zod_1.z.string().describe("Phase ID to get schema from (use list_workflow_phases to get available phases)"),
63
+ compact: zod_1.z.boolean().optional().describe("Return compact summary instead of full field details (default: false)"),
63
64
  }),
64
65
  async execute(args, context) {
65
66
  try {
@@ -78,6 +79,21 @@ exports.getWorkflowSchemaTool = {
78
79
  // Get complete field definitions from workflow (includes keys and all metadata)
79
80
  const workflowFields = workflow.fields || {};
80
81
  const fieldsOrder = workflow.fieldsOrder || Object.keys(workflowFields);
82
+ // Compact mode: brief summary for chat agents WITH field IDs (required for insights)
83
+ if (args.compact) {
84
+ const fieldLines = fieldsOrder.map((fieldId) => {
85
+ const field = workflowFields[fieldId];
86
+ if (!field)
87
+ return null;
88
+ return `- ${field.label || 'Unnamed'} (${field.type}): fieldId="${fieldId}"`;
89
+ }).filter(Boolean);
90
+ return {
91
+ content: [{
92
+ type: "text",
93
+ text: `๐Ÿ“‹ **${workflow.name}** (workflowId: ${args.workflowId})\n\n**Fields:**\n${fieldLines.join('\n')}`,
94
+ }],
95
+ };
96
+ }
81
97
  let responseText = `๐Ÿ“‹ **WORKFLOW SCHEMA** for "${workflow.name}":\n\n`;
82
98
  if (schemaData.name) {
83
99
  responseText += `**Name Field:**\n`;
@@ -336,45 +352,7 @@ exports.listWorkflowsTool = {
336
352
  // ============================================================================
337
353
  // TOOL 1: INSTALL WORKFLOW
338
354
  // ============================================================================
339
- const installWorkflowDescription = `๐Ÿงช [PLAYGROUND] Install workflow(s) from template
340
-
341
- โš ๏ธ **MANDATORY: YOU MUST LOAD SKILL FIRST**
342
- **BEFORE calling this tool, you are REQUIRED to:**
343
- 1. Load the skill: \`get_skill({ skillName: "install-workflow-skill" })\`
344
- 2. Read the complete documentation
345
- 3. Follow the examples and patterns shown in the skill
346
- **FAILURE TO LOAD THE SKILL FIRST IS AN ERROR**
347
-
348
- โš ๏ธ COPY THIS EXACT STRUCTURE (modify values only):
349
- \`\`\`json
350
- {
351
- "workflowTemplates": [{
352
- "name": "My Workflow Name",
353
- "fields": {
354
- "_1000": { "label": "Title", "type": "text" }
355
- },
356
- "phases": {
357
- "_2000": { "name": "To Do", "isInitial": true }
358
- }
359
- }]
360
- }
361
- \`\`\`
362
-
363
- **CRITICAL RULES (breaking these causes API rejection):**
364
- 1. โœ… MUST wrap in \`workflowTemplates\` array
365
- 2. โœ… MUST use objects for fields/phases: \`{ "_1000": {...} }\` NOT arrays \`[{...}]\`
366
- 3. โœ… MUST use \`label\` in fields (NOT "name")
367
- 4. โœ… MUST use \`data\` for options (NOT "options")
368
- 5. โœ… MUST use template IDs: \`_0001\`, \`_1000\`, \`_2000\` (underscore + 4 digits)
369
-
370
- **Field properties allowed:**
371
- - \`label\` (required) - Display name
372
- - \`type\` (required) - Field type: text, date, numeric, textpredefinedoptions, activitylink
373
- - \`key\` (recommended) - API key for readable names
374
- - \`data\` (for dropdowns/links) - Array of options or workflow IDs
375
- - \`required\` (optional) - Boolean
376
-
377
- **Workflow = DB table, Fields = Columns, Activities = Rows**`;
355
+ const installWorkflowDescription = `Install workflow from template`;
378
356
  const installWorkflowSchema = zod_1.z.object({
379
357
  workspaceId: zod_1.z
380
358
  .string()
@@ -534,27 +512,7 @@ exports.installWorkflowTool = {
534
512
  // ============================================================================
535
513
  // TOOL 2: REMOVE WORKFLOW
536
514
  // ============================================================================
537
- const removeWorkflowDescription = `๐Ÿงช [PLAYGROUND] Remove workflow - โš ๏ธ DANGER: Permanently deletes workflow, activities, and discussions
538
-
539
- โš ๏ธ **MANDATORY: GATHER COMPLETE CONTEXT BEFORE CALLING THIS TOOL**
540
- **BEFORE calling this tool, you are REQUIRED to:**
541
- 1. Load the skill: \`get_skill({ skillName: "remove-workflow-skill" })\`
542
- 2. Fetch workflow details with \`list_workflows\` to get:
543
- - Workflow name and ID
544
- - Workspace ID and name
545
- - Activity count
546
- - Relationships (which workflows link to this one)
547
- 3. Show comprehensive confirmation message including:
548
- - Workspace ID and name
549
- - Workflow ID and name
550
- - Exact count of activities that will be deleted
551
- - Impact analysis - which other workflows will be affected (broken activity links)
552
- - Clear irreversibility warning
553
- 4. Wait for explicit user confirmation
554
- **FAILURE TO GATHER AND SHOW THIS CONTEXT IS AN ERROR**
555
-
556
- **Required**: workflowId
557
- **Permission**: Workspace administrator`;
515
+ const removeWorkflowDescription = `Delete workflow permanently`;
558
516
  const removeWorkflowSchema = zod_1.z.object({
559
517
  workflowId: zod_1.z
560
518
  .string()
@@ -695,35 +653,7 @@ exports.removeWorkflowTool = {
695
653
  // ============================================================================
696
654
  // TOOL 3: UPDATE WORKFLOW FIELD
697
655
  // ============================================================================
698
- const updateWorkflowFieldDescription = `๐Ÿงช [PLAYGROUND] Update workflow field - Modify field properties and configure function fields
699
-
700
- โš ๏ธ **MANDATORY: YOU MUST LOAD SKILL FIRST**
701
- **BEFORE calling this tool, you are REQUIRED to:**
702
- 1. Load the skill: \`get_skill({ skillName: "update-workflow-field-skill" })\`
703
- 2. Read the examples and best practices
704
- 3. Understand function fields and field type restrictions
705
- **FAILURE TO LOAD THE SKILL FIRST IS AN ERROR**
706
-
707
- **Required Parameters**:
708
- - workflowId: Workflow ID
709
- - fieldId: Field ID to update
710
- - fieldData: Properties to update (label, key, type, data, required, function, functionVariables, functionEnabled)
711
-
712
- **IMPORTANT - Updating Labels Only:**
713
- When updating ONLY the label (not the key), set \`autoGenerateKey: false\` to prevent key regeneration:
714
- - โœ… Correct: \`{ fieldData: { label: "Nationality" }, autoGenerateKey: false }\`
715
- - โŒ Wrong: \`{ fieldData: { label: "Nationality" } }\` (will try to regenerate key and may fail)
716
-
717
- **Auto-generate key**: When creating NEW keys, set label only and key auto-generates as camelCase ("Start Date" โ†’ "startDate")
718
-
719
- **Field Type Changes**: Avoid changing field types (text โ†’ country, etc.) as this may be restricted by the API
720
-
721
- **Use Cases**: Add keys, update labels, update activitylink targets, configure calculated fields, modify options
722
-
723
- **Examples**:
724
- - Update label only: \`update_workflow_field({ workflowId: "xxx", fieldId: "yyy", fieldData: { label: "New Label" }, autoGenerateKey: false })\`
725
- - Update label and options: \`update_workflow_field({ workflowId: "xxx", fieldId: "yyy", fieldData: { label: "Status", data: ["Active", "Inactive"] }, autoGenerateKey: false })\`
726
- - Add new key to field: \`update_workflow_field({ workflowId: "xxx", fieldId: "yyy", fieldData: { label: "My Field" }, autoGenerateKey: true })\` (generates key: "myField")`;
656
+ const updateWorkflowFieldDescription = `Update workflow field properties`;
727
657
  const updateWorkflowFieldSchema = zod_1.z.object({
728
658
  workflowId: zod_1.z
729
659
  .string()
@@ -857,11 +787,7 @@ exports.updateWorkflowFieldTool = {
857
787
  // ============================================================================
858
788
  // TOOL 4: TEST FUNCTION FIELD
859
789
  // ============================================================================
860
- const testFunctionFieldDescription = `๐Ÿงช [PLAYGROUND] Test function field - Dry-run test function code against real activity before saving
861
-
862
- **Required**: activityId, fieldId, function (JavaScript accessing dep.variableName)
863
- **Returns**: Result value, resolved dependencies, execution stats, or detailed errors
864
- **Workflow**: Configure with update_workflow_field โ†’ test โ†’ fix โ†’ re-test โ†’ deploy`;
790
+ const testFunctionFieldDescription = `Test function field code against activity`;
865
791
  const testFunctionFieldSchema = zod_1.z.object({
866
792
  activityId: zod_1.z
867
793
  .string()
@@ -72,12 +72,14 @@ export declare class HailerApiClient {
72
72
  /**
73
73
  * Join an activity's discussion by following the activity - uses Socket API
74
74
  * This is the recommended way to join activity discussions.
75
- * Returns true if now following, false if was already following and toggled off.
75
+ * Returns true if now following.
76
+ * IMPORTANT: activities.follow is a toggle, so if we accidentally toggle OFF, we toggle back ON.
76
77
  */
77
78
  joinActivityDiscussion(activityId: string): Promise<boolean>;
78
79
  /**
79
80
  * Leave an activity's discussion by unfollowing the activity - uses Socket API
80
- * Returns true if successfully unfollowed, false if was already not following.
81
+ * Returns true if successfully unfollowed.
82
+ * IMPORTANT: activities.follow is a toggle, so if we accidentally toggle ON, we toggle back OFF.
81
83
  */
82
84
  leaveActivityDiscussion(activityId: string): Promise<boolean>;
83
85
  /**
@@ -218,25 +218,39 @@ class HailerApiClient {
218
218
  /**
219
219
  * Join an activity's discussion by following the activity - uses Socket API
220
220
  * This is the recommended way to join activity discussions.
221
- * Returns true if now following, false if was already following and toggled off.
221
+ * Returns true if now following.
222
+ * IMPORTANT: activities.follow is a toggle, so if we accidentally toggle OFF, we toggle back ON.
222
223
  */
223
224
  async joinActivityDiscussion(activityId) {
224
- // activities.follow toggles the state, but we need to check current state first
225
- const activity = await this.fetchActivityById(activityId);
226
- // If not already following, call activities.follow to join
225
+ // activities.follow TOGGLES the state:
226
+ // - returns true = now following (joined)
227
+ // - returns false = now NOT following (left)
227
228
  const result = await this.request('activities.follow', [activityId]);
228
- return result; // true if now following, false if unfollowed
229
+ if (result === false) {
230
+ // Oops! We were already following and just toggled OFF (left).
231
+ // Toggle again to re-join.
232
+ this.logger.debug('Toggle resulted in unfollow, re-toggling to join', { activityId });
233
+ await this.request('activities.follow', [activityId]);
234
+ }
235
+ return true; // We're now following either way
229
236
  }
230
237
  /**
231
238
  * Leave an activity's discussion by unfollowing the activity - uses Socket API
232
- * Returns true if successfully unfollowed, false if was already not following.
239
+ * Returns true if successfully unfollowed.
240
+ * IMPORTANT: activities.follow is a toggle, so if we accidentally toggle ON, we toggle back OFF.
233
241
  */
234
242
  async leaveActivityDiscussion(activityId) {
235
- // activities.follow toggles the state
236
- const activity = await this.fetchActivityById(activityId);
237
- // If already following, call activities.follow to unfollow
243
+ // activities.follow TOGGLES the state:
244
+ // - returns true = now following (joined)
245
+ // - returns false = now NOT following (left)
238
246
  const result = await this.request('activities.follow', [activityId]);
239
- return !result; // Inverse: true if now unfollowed, false if followed
247
+ if (result === true) {
248
+ // Oops! We were NOT following and just toggled ON (joined).
249
+ // Toggle again to leave.
250
+ this.logger.debug('Toggle resulted in follow, re-toggling to leave', { activityId });
251
+ await this.request('activities.follow', [activityId]);
252
+ }
253
+ return true; // We're now NOT following either way
240
254
  }
241
255
  /**
242
256
  * Leave a discussion directly - uses Socket API
@@ -16,6 +16,10 @@ export interface MCPServerConfig {
16
16
  port: number;
17
17
  corsOrigins: string[];
18
18
  toolRegistry: ToolRegistry;
19
+ getDaemonStatus?: () => Array<{
20
+ botId: string;
21
+ state: any;
22
+ }> | null;
19
23
  }
20
24
  export declare class MCPServerService {
21
25
  private app;
@@ -65,10 +65,13 @@ class MCPServerService {
65
65
  });
66
66
  res.on('finish', () => {
67
67
  const duration = Date.now() - start;
68
- requestLogger.info('Request completed', {
69
- statusCode: res.statusCode,
70
- duration
71
- });
68
+ // Log at debug level for successful requests, info for errors
69
+ if (res.statusCode >= 400) {
70
+ requestLogger.info('Request failed', { statusCode: res.statusCode, duration });
71
+ }
72
+ else {
73
+ requestLogger.debug('Request completed', { statusCode: res.statusCode, duration });
74
+ }
72
75
  });
73
76
  }
74
77
  // Add logger to request for route handlers
@@ -88,6 +91,22 @@ class MCPServerService {
88
91
  };
89
92
  res.json(health);
90
93
  });
94
+ // Daemon status endpoint - monitor LLM context
95
+ this.app.get('/daemon/status', (_, res) => {
96
+ if (!this.config.getDaemonStatus) {
97
+ res.status(404).json({ error: 'Daemon mode not enabled' });
98
+ return;
99
+ }
100
+ const status = this.config.getDaemonStatus();
101
+ if (!status) {
102
+ res.status(503).json({ error: 'Daemon not running' });
103
+ return;
104
+ }
105
+ res.json({
106
+ timestamp: new Date().toISOString(),
107
+ daemons: status
108
+ });
109
+ });
91
110
  // MCP Protocol endpoint - JSON-RPC 2.0 over SSE
92
111
  this.app.post('/api/mcp', async (req, res) => {
93
112
  req.logger.debug('MCP request received', { method: req.body?.method });
@@ -202,6 +221,7 @@ class MCPServerService {
202
221
  this.logger.info('Routes configured', {
203
222
  routes: [
204
223
  '/health - Server health check',
224
+ '/daemon/status - Daemon context monitor',
205
225
  '/api/mcp - MCP protocol (JSON-RPC 2.0)'
206
226
  ]
207
227
  });