@hailer/mcp 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/.claude/commands/tool-builder.md +37 -0
  2. package/.claude/commands/ws-pull.md +44 -0
  3. package/.claude/settings.json +8 -0
  4. package/.claude/settings.local.json +49 -0
  5. package/.claude/skills/activity-api/SKILL.md +96 -0
  6. package/.claude/skills/activity-api/references/activity-endpoints.md +845 -0
  7. package/.claude/skills/add-app-member-skill/SKILL.md +977 -0
  8. package/.claude/skills/agent-building/SKILL.md +243 -0
  9. package/.claude/skills/agent-building/references/architecture-patterns.md +446 -0
  10. package/.claude/skills/agent-building/references/code-examples.md +587 -0
  11. package/.claude/skills/agent-building/references/implementation-guide.md +619 -0
  12. package/.claude/skills/app-api/SKILL.md +219 -0
  13. package/.claude/skills/app-api/references/app-endpoints.md +759 -0
  14. package/.claude/skills/building-hailer-apps-skill/SKILL.md +548 -0
  15. package/.claude/skills/create-app-skill/SKILL.md +1101 -0
  16. package/.claude/skills/create-insight-skill/SKILL.md +1317 -0
  17. package/.claude/skills/get-insight-data-skill/SKILL.md +1053 -0
  18. package/.claude/skills/hailer-api/SKILL.md +283 -0
  19. package/.claude/skills/hailer-api/references/activities.md +620 -0
  20. package/.claude/skills/hailer-api/references/authentication.md +216 -0
  21. package/.claude/skills/hailer-api/references/datasets.md +437 -0
  22. package/.claude/skills/hailer-api/references/files.md +301 -0
  23. package/.claude/skills/hailer-api/references/insights.md +469 -0
  24. package/.claude/skills/hailer-api/references/workflows.md +720 -0
  25. package/.claude/skills/hailer-api/references/workspaces-users.md +445 -0
  26. package/.claude/skills/insight-api/SKILL.md +185 -0
  27. package/.claude/skills/insight-api/references/insight-endpoints.md +514 -0
  28. package/.claude/skills/install-workflow-skill/SKILL.md +1056 -0
  29. package/.claude/skills/list-apps-skill/SKILL.md +1010 -0
  30. package/.claude/skills/list-workflows-minimal-skill/SKILL.md +992 -0
  31. package/.claude/skills/local-first-skill/SKILL.md +570 -0
  32. package/.claude/skills/mcp-tools/SKILL.md +419 -0
  33. package/.claude/skills/mcp-tools/references/api-endpoints.md +499 -0
  34. package/.claude/skills/mcp-tools/references/data-structures.md +554 -0
  35. package/.claude/skills/mcp-tools/references/implementation-patterns.md +717 -0
  36. package/.claude/skills/preview-insight-skill/SKILL.md +1290 -0
  37. package/.claude/skills/publish-hailer-app-skill/SKILL.md +453 -0
  38. package/.claude/skills/remove-app-member-skill/SKILL.md +671 -0
  39. package/.claude/skills/remove-app-skill/SKILL.md +985 -0
  40. package/.claude/skills/remove-insight-skill/SKILL.md +1011 -0
  41. package/.claude/skills/remove-workflow-skill/SKILL.md +920 -0
  42. package/.claude/skills/scaffold-hailer-app-skill/SKILL.md +1034 -0
  43. package/.claude/skills/skill-testing/README.md +137 -0
  44. package/.claude/skills/skill-testing/SKILL.md +348 -0
  45. package/.claude/skills/skill-testing/references/test-patterns.md +705 -0
  46. package/.claude/skills/skill-testing/references/testing-guide.md +603 -0
  47. package/.claude/skills/skill-testing/references/validation-checklist.md +537 -0
  48. package/.claude/skills/tool-builder/SKILL.md +328 -0
  49. package/.claude/skills/update-app-skill/SKILL.md +970 -0
  50. package/.claude/skills/update-workflow-field-skill/SKILL.md +1098 -0
  51. package/.env.example +81 -0
  52. package/.mcp.json +13 -0
  53. package/README.md +297 -0
  54. package/dist/app.d.ts +4 -0
  55. package/dist/app.js +74 -0
  56. package/dist/cli.d.ts +3 -0
  57. package/dist/cli.js +5 -0
  58. package/dist/client/adaptive-documentation-bot.d.ts +108 -0
  59. package/dist/client/adaptive-documentation-bot.js +475 -0
  60. package/dist/client/adaptive-documentation-types.d.ts +66 -0
  61. package/dist/client/adaptive-documentation-types.js +9 -0
  62. package/dist/client/agent-activity-bot.d.ts +51 -0
  63. package/dist/client/agent-activity-bot.js +166 -0
  64. package/dist/client/agent-tracker.d.ts +499 -0
  65. package/dist/client/agent-tracker.js +659 -0
  66. package/dist/client/description-updater.d.ts +56 -0
  67. package/dist/client/description-updater.js +259 -0
  68. package/dist/client/log-parser.d.ts +72 -0
  69. package/dist/client/log-parser.js +387 -0
  70. package/dist/client/mcp-client.d.ts +50 -0
  71. package/dist/client/mcp-client.js +532 -0
  72. package/dist/client/message-processor.d.ts +35 -0
  73. package/dist/client/message-processor.js +352 -0
  74. package/dist/client/multi-bot-manager.d.ts +24 -0
  75. package/dist/client/multi-bot-manager.js +74 -0
  76. package/dist/client/providers/anthropic-provider.d.ts +19 -0
  77. package/dist/client/providers/anthropic-provider.js +631 -0
  78. package/dist/client/providers/llm-provider.d.ts +47 -0
  79. package/dist/client/providers/llm-provider.js +367 -0
  80. package/dist/client/providers/openai-provider.d.ts +23 -0
  81. package/dist/client/providers/openai-provider.js +621 -0
  82. package/dist/client/simple-llm-caller.d.ts +19 -0
  83. package/dist/client/simple-llm-caller.js +100 -0
  84. package/dist/client/skill-generator.d.ts +81 -0
  85. package/dist/client/skill-generator.js +386 -0
  86. package/dist/client/test-adaptive-bot.d.ts +9 -0
  87. package/dist/client/test-adaptive-bot.js +82 -0
  88. package/dist/client/token-pricing.d.ts +38 -0
  89. package/dist/client/token-pricing.js +127 -0
  90. package/dist/client/token-tracker.d.ts +232 -0
  91. package/dist/client/token-tracker.js +457 -0
  92. package/dist/client/token-usage-bot.d.ts +53 -0
  93. package/dist/client/token-usage-bot.js +153 -0
  94. package/dist/client/tool-executor.d.ts +69 -0
  95. package/dist/client/tool-executor.js +159 -0
  96. package/dist/client/tool-schema-loader.d.ts +60 -0
  97. package/dist/client/tool-schema-loader.js +178 -0
  98. package/dist/client/types.d.ts +69 -0
  99. package/dist/client/types.js +7 -0
  100. package/dist/config.d.ts +162 -0
  101. package/dist/config.js +296 -0
  102. package/dist/core.d.ts +26 -0
  103. package/dist/core.js +147 -0
  104. package/dist/lib/context-manager.d.ts +111 -0
  105. package/dist/lib/context-manager.js +431 -0
  106. package/dist/lib/logger.d.ts +74 -0
  107. package/dist/lib/logger.js +277 -0
  108. package/dist/lib/materialize.d.ts +3 -0
  109. package/dist/lib/materialize.js +101 -0
  110. package/dist/lib/normalizedName.d.ts +7 -0
  111. package/dist/lib/normalizedName.js +48 -0
  112. package/dist/lib/prompt-length-manager.d.ts +81 -0
  113. package/dist/lib/prompt-length-manager.js +457 -0
  114. package/dist/lib/terminal-prompt.d.ts +9 -0
  115. package/dist/lib/terminal-prompt.js +108 -0
  116. package/dist/mcp/UserContextCache.d.ts +56 -0
  117. package/dist/mcp/UserContextCache.js +163 -0
  118. package/dist/mcp/auth.d.ts +2 -0
  119. package/dist/mcp/auth.js +29 -0
  120. package/dist/mcp/hailer-clients.d.ts +42 -0
  121. package/dist/mcp/hailer-clients.js +246 -0
  122. package/dist/mcp/signal-handler.d.ts +45 -0
  123. package/dist/mcp/signal-handler.js +317 -0
  124. package/dist/mcp/tool-registry.d.ts +100 -0
  125. package/dist/mcp/tool-registry.js +306 -0
  126. package/dist/mcp/tools/activity.d.ts +15 -0
  127. package/dist/mcp/tools/activity.js +955 -0
  128. package/dist/mcp/tools/app.d.ts +20 -0
  129. package/dist/mcp/tools/app.js +1488 -0
  130. package/dist/mcp/tools/discussion.d.ts +19 -0
  131. package/dist/mcp/tools/discussion.js +950 -0
  132. package/dist/mcp/tools/file.d.ts +15 -0
  133. package/dist/mcp/tools/file.js +119 -0
  134. package/dist/mcp/tools/insight.d.ts +17 -0
  135. package/dist/mcp/tools/insight.js +806 -0
  136. package/dist/mcp/tools/skill.d.ts +10 -0
  137. package/dist/mcp/tools/skill.js +279 -0
  138. package/dist/mcp/tools/user.d.ts +10 -0
  139. package/dist/mcp/tools/user.js +108 -0
  140. package/dist/mcp/tools/workflow-template.d.ts +19 -0
  141. package/dist/mcp/tools/workflow-template.js +822 -0
  142. package/dist/mcp/tools/workflow.d.ts +18 -0
  143. package/dist/mcp/tools/workflow.js +1362 -0
  144. package/dist/mcp/utils/api-errors.d.ts +45 -0
  145. package/dist/mcp/utils/api-errors.js +160 -0
  146. package/dist/mcp/utils/data-transformers.d.ts +102 -0
  147. package/dist/mcp/utils/data-transformers.js +194 -0
  148. package/dist/mcp/utils/file-upload.d.ts +33 -0
  149. package/dist/mcp/utils/file-upload.js +148 -0
  150. package/dist/mcp/utils/hailer-api-client.d.ts +120 -0
  151. package/dist/mcp/utils/hailer-api-client.js +323 -0
  152. package/dist/mcp/utils/index.d.ts +13 -0
  153. package/dist/mcp/utils/index.js +39 -0
  154. package/dist/mcp/utils/logger.d.ts +42 -0
  155. package/dist/mcp/utils/logger.js +103 -0
  156. package/dist/mcp/utils/types.d.ts +286 -0
  157. package/dist/mcp/utils/types.js +7 -0
  158. package/dist/mcp/workspace-cache.d.ts +42 -0
  159. package/dist/mcp/workspace-cache.js +97 -0
  160. package/dist/mcp-server.d.ts +42 -0
  161. package/dist/mcp-server.js +280 -0
  162. package/package.json +56 -0
  163. package/tsconfig.json +23 -0
@@ -0,0 +1,1362 @@
1
+ "use strict";
2
+ /**
3
+ * Workflow Management Tools - Clean Architecture
4
+ *
5
+ * Tools for managing Hailer workflows (install, remove, update fields, test functions).
6
+ * These are PLAYGROUND tools requiring workspace administrator permissions.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.updateWorkflowPhaseTool = exports.countActivitiesTool = exports.listWorkflowsMinimalTool = exports.testFunctionFieldTool = exports.updateWorkflowFieldTool = exports.removeWorkflowTool = exports.installWorkflowTool = exports.listWorkflowsTool = exports.listWorkflowPhasesTool = exports.getWorkflowSchemaTool = void 0;
10
+ const zod_1 = require("zod");
11
+ const tool_registry_1 = require("../tool-registry");
12
+ const logger_1 = require("../../lib/logger");
13
+ const logger = (0, logger_1.createLogger)({ component: 'workflow-tools' });
14
+ // ============================================================================
15
+ // HELPER FUNCTIONS
16
+ // ============================================================================
17
+ /**
18
+ * Convert field label to camelCase key
19
+ * Example: "Start Date" -> "startDate", "Related To" -> "relatedTo"
20
+ */
21
+ function labelToCamelCase(label) {
22
+ return label
23
+ .trim()
24
+ .split(/\s+/)
25
+ .map((word, index) => {
26
+ const cleaned = word.replace(/[^\w]/g, ''); // Remove non-word characters
27
+ if (index === 0) {
28
+ return cleaned.toLowerCase();
29
+ }
30
+ return cleaned.charAt(0).toUpperCase() + cleaned.slice(1).toLowerCase();
31
+ })
32
+ .join('');
33
+ }
34
+ /**
35
+ * Extract error from v2.activities.evaluate response
36
+ * Checks three possible error locations in the API response
37
+ */
38
+ function extractEvaluationError(result, fieldId) {
39
+ const evaluatedField = result?.activity?.fields?.find((f) => f.id === fieldId);
40
+ return evaluatedField?.error
41
+ || result?.meta?.fields?.[fieldId]?.error
42
+ || result?.meta?.error
43
+ || null;
44
+ }
45
+ /**
46
+ * Parse JavaScript error type from error message
47
+ */
48
+ function parseErrorType(errorMessage) {
49
+ const match = errorMessage.match(/^(SyntaxError|ReferenceError|TypeError)/);
50
+ return match ? match[1] : 'Error';
51
+ }
52
+ // ============================================================================
53
+ // READ TOOLS - Workflow Schema and Information
54
+ // ============================================================================
55
+ const getWorkflowSchemaDescription = `šŸ“‹ Get workflow field schema and structure - REQUIRED before using list_activities to understand available fields. Shows all field IDs, labels, types, and options. Requires both workflowId and phaseId (use list_workflow_phases first).`;
56
+ exports.getWorkflowSchemaTool = {
57
+ name: 'get_workflow_schema',
58
+ group: tool_registry_1.ToolGroup.READ,
59
+ description: getWorkflowSchemaDescription,
60
+ schema: zod_1.z.object({
61
+ workflowId: zod_1.z.string().describe("Workflow ID to get schema from"),
62
+ phaseId: zod_1.z.string().describe("Phase ID to get schema from (use list_workflow_phases to get available phases)"),
63
+ }),
64
+ async execute(args, context) {
65
+ try {
66
+ // Fetch complete workflow definition from API (includes field keys)
67
+ const initData = await context.hailer.request('v2.core.init', [['processes']]);
68
+ const workflow = initData.processes?.find(p => p._id === args.workflowId);
69
+ if (!workflow) {
70
+ return {
71
+ content: [{
72
+ type: "text",
73
+ text: `āŒ Workflow "${args.workflowId}" not found`,
74
+ }],
75
+ };
76
+ }
77
+ // Get schema for field validation/requirements (optional, mainly for name field info)
78
+ const schemaData = await context.hailer.getWorkflowSchema(args.workflowId, args.phaseId);
79
+ // Get complete field definitions from workflow (includes keys and all metadata)
80
+ const workflowFields = workflow.fields || {};
81
+ const fieldsOrder = workflow.fieldsOrder || Object.keys(workflowFields);
82
+ let responseText = `šŸ“‹ **WORKFLOW SCHEMA** for "${workflow.name}":\n\n`;
83
+ if (schemaData.name) {
84
+ responseText += `**Name Field:**\n`;
85
+ responseText += `- Type: ${schemaData.name.type}\n`;
86
+ responseText += `- Required: ${schemaData.name.required || false}\n`;
87
+ if (schemaData.name.placeholder) {
88
+ responseText += `- Placeholder: "${schemaData.name.placeholder}"\n`;
89
+ }
90
+ responseText += `\n`;
91
+ }
92
+ // Use fieldsOrder to display fields in correct order
93
+ if (fieldsOrder.length > 0) {
94
+ responseText += `**Available Fields (${fieldsOrder.length} total):**\n\n`;
95
+ fieldsOrder.forEach((fieldId, index) => {
96
+ const field = workflowFields[fieldId];
97
+ if (!field)
98
+ return;
99
+ responseText += `${index + 1}. **${field.label || 'Unnamed Field'}**\n`;
100
+ responseText += ` - Field ID: \`${fieldId}\`\n`;
101
+ if (field.key)
102
+ responseText += ` - Key: \`${field.key}\`\n`;
103
+ responseText += ` - Type: ${field.type}`;
104
+ if (field.subtype)
105
+ responseText += ` (${field.subtype})`;
106
+ responseText += `\n`;
107
+ if (field.required)
108
+ responseText += ` - Required: Yes\n`;
109
+ if (field.description)
110
+ responseText += ` - Description: ${field.description}\n`;
111
+ if (field.placeholder)
112
+ responseText += ` - Placeholder: "${field.placeholder}"\n`;
113
+ if (field.data && field.data.length > 0) {
114
+ responseText += ` - Options: ${field.data.slice(0, 3).join(', ')}`;
115
+ if (field.data.length > 3)
116
+ responseText += ` (and ${field.data.length - 3} more)`;
117
+ responseText += `\n`;
118
+ }
119
+ responseText += `\n`;
120
+ });
121
+ }
122
+ responseText += `šŸ’” **USAGE:**\n`;
123
+ responseText += `- Use field IDs (or keys if available) in the 'fields' parameter of list_activities\n`;
124
+ responseText += `- **Select ONLY 2-3 essential fields** to minimize token costs\n`;
125
+ const firstFieldId = fieldsOrder[0];
126
+ const secondFieldId = fieldsOrder[1];
127
+ const firstFieldKey = firstFieldId && workflowFields[firstFieldId]?.key;
128
+ const secondFieldKey = secondFieldId && workflowFields[secondFieldId]?.key;
129
+ if (firstFieldId && secondFieldId) {
130
+ responseText += `- Example with IDs: fields: ["${firstFieldId}", "${secondFieldId}"] (just 2 fields)\n`;
131
+ if (firstFieldKey && secondFieldKey) {
132
+ responseText += `- Example with keys: fields: ["${firstFieldKey}", "${secondFieldKey}"] (more readable)\n`;
133
+ }
134
+ }
135
+ responseText += `- For listings: name + key metric (price/status) is usually sufficient\n`;
136
+ responseText += `- Only add description/detail fields when user explicitly needs them`;
137
+ return {
138
+ content: [{
139
+ type: "text",
140
+ text: responseText,
141
+ }],
142
+ };
143
+ }
144
+ catch (error) {
145
+ logger.error("Failed to get workflow schema", error);
146
+ return {
147
+ content: [{
148
+ type: "text",
149
+ text: `āŒ Failed to get workflow schema: ${error instanceof Error ? error.message : String(error)}`,
150
+ }],
151
+ };
152
+ }
153
+ },
154
+ };
155
+ const listWorkflowPhasesDescription = `šŸ“Š List available phases in a workflow - Required before using list_activities to get phase IDs. Use this first to see all phases, then call list_activities for each phase to get complete workflow overview. TIP: Also call get_workflow_schema to understand field structure.`;
156
+ exports.listWorkflowPhasesTool = {
157
+ name: 'list_workflow_phases',
158
+ group: tool_registry_1.ToolGroup.READ,
159
+ description: listWorkflowPhasesDescription,
160
+ schema: zod_1.z.object({
161
+ workflowId: zod_1.z.string().describe("Workflow ID to get phases from"),
162
+ }),
163
+ async execute(args, context) {
164
+ try {
165
+ // Fetch workflow definition directly from API
166
+ const initData = await context.hailer.request('v2.core.init', [["processes"]]);
167
+ const workflowData = initData.processes?.find(p => p._id === args.workflowId);
168
+ if (!workflowData) {
169
+ return {
170
+ content: [{
171
+ type: "text",
172
+ text: `āŒ Workflow "${args.workflowId}" not found`,
173
+ }],
174
+ };
175
+ }
176
+ const phases = workflowData.phases || {};
177
+ const phasesOrder = workflowData.phasesOrder || [];
178
+ let responseText = `šŸ“Š **WORKFLOW PHASES** for "${workflowData.name}":\n\n`;
179
+ if (Object.keys(phases).length === 0) {
180
+ responseText += `āŒ No phases found in workflow "${workflowData.name}".`;
181
+ return {
182
+ content: [{ type: "text", text: responseText }],
183
+ };
184
+ }
185
+ responseText += `Found ${Object.keys(phases).length} phases:\n\n`;
186
+ const orderedPhaseIds = phasesOrder.length > 0 ? phasesOrder : Object.keys(phases);
187
+ orderedPhaseIds.forEach((phaseId, index) => {
188
+ const phase = phases[phaseId];
189
+ if (phase) {
190
+ responseText += `${index + 1}. **${phase.name}**\n`;
191
+ responseText += ` - Phase ID: \`${phase._id}\`\n`;
192
+ if (phase.description) {
193
+ responseText += ` - Description: ${phase.description}\n`;
194
+ }
195
+ if (phase.color) {
196
+ responseText += ` - Color: ${phase.color}\n`;
197
+ }
198
+ if (phase.isInitial) {
199
+ responseText += ` - Initial Phase: Yes\n`;
200
+ }
201
+ if (phase.isEndpoint) {
202
+ responseText += ` - Endpoint Phase: Yes\n`;
203
+ }
204
+ responseText += `\n`;
205
+ }
206
+ });
207
+ responseText += `šŸ’” **USAGE:** Use any of these Phase IDs with the \`list_activities\` tool to get activities in that phase.\n\n`;
208
+ responseText += `šŸ” **FOR COMPREHENSIVE WORKFLOW VIEW:**\n`;
209
+ responseText += `- To see ALL activities across the entire workflow, call \`list_activities\` for each phase ID above\n`;
210
+ responseText += `- Each phase contains different activities at different stages of the workflow\n`;
211
+ responseText += `- Combine results from all phases to get the complete picture of the workflow\n`;
212
+ responseText += `\n**Example workflow:**\n`;
213
+ responseText += `1. First call: \`list_activities\` with first phase ID to see new/pending activities\n`;
214
+ responseText += `2. Second call: \`list_activities\` with second phase ID to see completed activities\n`;
215
+ responseText += `3. Continue for all phases to see the full workflow status`;
216
+ return {
217
+ content: [{
218
+ type: "text",
219
+ text: responseText,
220
+ }],
221
+ };
222
+ }
223
+ catch (error) {
224
+ logger.error("Failed to list workflow phases", error);
225
+ return {
226
+ content: [{
227
+ type: "text",
228
+ text: `āŒ Failed to list workflow phases: ${error instanceof Error ? error.message : String(error)}`,
229
+ }],
230
+ };
231
+ }
232
+ },
233
+ };
234
+ const listWorkflowsDescription = `List workflows - Get workflow info with optional detailed data`;
235
+ exports.listWorkflowsTool = {
236
+ name: 'list_workflows',
237
+ group: tool_registry_1.ToolGroup.READ,
238
+ description: listWorkflowsDescription,
239
+ schema: zod_1.z.object({
240
+ workspace: zod_1.z.string().optional().describe("Optional workspace ID or name"),
241
+ includeRelationships: zod_1.z.coerce.boolean().optional().default(true).describe("Show ActivityLink relationships between workflows"),
242
+ }),
243
+ async execute(args, context) {
244
+ logger.debug('Listing workflows', {
245
+ apiKey: context.apiKey.substring(0, 8) + '...'
246
+ });
247
+ try {
248
+ // Fetch workflows directly from API
249
+ const initData = await context.hailer.request('v2.core.init', [["processes"]]);
250
+ let workflows = initData.processes || [];
251
+ if (args.workspace) {
252
+ // Filter by workspace ID or name
253
+ const workspaceFilter = args.workspace;
254
+ workflows = workflows.filter(w => w.cid === workspaceFilter ||
255
+ w.cid === workspaceFilter // Could add name-based lookup here if needed
256
+ );
257
+ }
258
+ const workflowSummary = workflows.map((workflow) => {
259
+ const activityLinkFields = Object.entries(workflow.fields ?? {})
260
+ .filter(([_, field]) => field.type === 'activitylink');
261
+ const initialPhases = Object.values(workflow.phases || {})
262
+ .filter(phase => phase.isInitial)
263
+ .map(phase => ({
264
+ id: phase._id,
265
+ name: phase.name
266
+ }));
267
+ // Calculate linksTo from activitylink fields
268
+ const linksTo = activityLinkFields.flatMap(([_, field]) => field.data || []);
269
+ return {
270
+ id: workflow._id,
271
+ name: workflow.name,
272
+ workspaceId: workflow.cid,
273
+ activityCount: workflow.createdActivities || 0,
274
+ isStarred: workflow.isStarred || false,
275
+ hasActivityLinks: activityLinkFields.length > 0,
276
+ linksTo,
277
+ linkedFrom: [], // Would need to scan all workflows - skip for now
278
+ initialPhases: initialPhases,
279
+ };
280
+ });
281
+ let responseText = `šŸ“‹ **${workflows.length} Workflows Found**\n\n`;
282
+ responseText += JSON.stringify(workflowSummary, null, 2);
283
+ if (args.includeRelationships) {
284
+ const relationships = [];
285
+ for (const workflow of workflows) {
286
+ const activityLinkFields = Object.entries(workflow.fields ?? {})
287
+ .filter(([_, field]) => field.type === 'activitylink')
288
+ .map(([fieldId, field]) => ({
289
+ id: fieldId,
290
+ label: field.label,
291
+ key: field.key,
292
+ targetWorkflowIds: field.data || [],
293
+ required: field.required || false,
294
+ }));
295
+ for (const field of activityLinkFields) {
296
+ for (const targetWorkflowId of field.targetWorkflowIds) {
297
+ const targetWorkflow = workflows.find(w => w._id === targetWorkflowId);
298
+ relationships.push({
299
+ sourceWorkflowId: workflow._id,
300
+ sourceWorkflowName: workflow.name,
301
+ targetWorkflowId,
302
+ targetWorkflowName: targetWorkflow?.name || 'Unknown',
303
+ fieldLabel: field.label,
304
+ fieldKey: field.key,
305
+ required: field.required,
306
+ });
307
+ }
308
+ }
309
+ }
310
+ if (relationships.length > 0) {
311
+ responseText += `\n\nšŸ”— **Workflow Relationships:**\n`;
312
+ relationships.forEach(rel => {
313
+ responseText += `• ${rel.sourceWorkflowName} → ${rel.targetWorkflowName} (${rel.fieldLabel})\n`;
314
+ });
315
+ }
316
+ }
317
+ responseText += `\n\nšŸ’” Use get_workflow_info with specific workflow ID for detailed information.`;
318
+ return {
319
+ content: [{
320
+ type: "text",
321
+ text: responseText,
322
+ }],
323
+ };
324
+ }
325
+ catch (error) {
326
+ logger.error("Error listing workflows", error);
327
+ return {
328
+ content: [{
329
+ type: "text",
330
+ text: `āŒ Error listing workflows: ${error instanceof Error ? error.message : String(error)}`,
331
+ }],
332
+ };
333
+ }
334
+ },
335
+ };
336
+ // ============================================================================
337
+ // PLAYGROUND TOOLS - Workflow Management (Admin/Dev)
338
+ // ============================================================================
339
+ // ============================================================================
340
+ // TOOL 1: INSTALL WORKFLOW
341
+ // ============================================================================
342
+ const installWorkflowDescription = `🧪 [PLAYGROUND] Install workflow(s) from template
343
+
344
+ āš ļø **MANDATORY: YOU MUST LOAD SKILL FIRST**
345
+ **BEFORE calling this tool, you are REQUIRED to:**
346
+ 1. Load the skill: \`get_skill({ skillName: "install-workflow-skill" })\`
347
+ 2. Read the complete documentation
348
+ 3. Follow the examples and patterns shown in the skill
349
+ **FAILURE TO LOAD THE SKILL FIRST IS AN ERROR**
350
+
351
+ āš ļø COPY THIS EXACT STRUCTURE (modify values only):
352
+ \`\`\`json
353
+ {
354
+ "workflowTemplates": [{
355
+ "name": "My Workflow Name",
356
+ "fields": {
357
+ "_1000": { "label": "Title", "type": "text" }
358
+ },
359
+ "phases": {
360
+ "_2000": { "name": "To Do", "isInitial": true }
361
+ }
362
+ }]
363
+ }
364
+ \`\`\`
365
+
366
+ **CRITICAL RULES (breaking these causes API rejection):**
367
+ 1. āœ… MUST wrap in \`workflowTemplates\` array
368
+ 2. āœ… MUST use objects for fields/phases: \`{ "_1000": {...} }\` NOT arrays \`[{...}]\`
369
+ 3. āœ… MUST use \`label\` in fields (NOT "name")
370
+ 4. āœ… MUST use \`data\` for options (NOT "options")
371
+ 5. āœ… MUST use template IDs: \`_0001\`, \`_1000\`, \`_2000\` (underscore + 4 digits)
372
+
373
+ **Field properties allowed:**
374
+ - \`label\` (required) - Display name
375
+ - \`type\` (required) - Field type: text, date, numeric, textpredefinedoptions, activitylink
376
+ - \`key\` (recommended) - API key for readable names
377
+ - \`data\` (for dropdowns/links) - Array of options or workflow IDs
378
+ - \`required\` (optional) - Boolean
379
+
380
+ **Workflow = DB table, Fields = Columns, Activities = Rows**`;
381
+ const installWorkflowSchema = zod_1.z.object({
382
+ workspaceId: zod_1.z
383
+ .string()
384
+ .optional()
385
+ .describe("Optional workspace ID or name - defaults to current workspace"),
386
+ workflowTemplates: zod_1.z
387
+ .array(zod_1.z.object({
388
+ _id: zod_1.z
389
+ .string()
390
+ .regex(/^_\d{4}$/, "Workflow _id must match pattern _0001, _0002, etc. (underscore + 4 digits)")
391
+ .optional()
392
+ .describe("Workflow _id for cross-references in activitylink fields (e.g., '_0001', '_0002')"),
393
+ name: zod_1.z.string().min(1).describe("Workflow name"),
394
+ description: zod_1.z.string().optional().describe("Workflow description"),
395
+ fields: zod_1.z
396
+ .record(zod_1.z.object({
397
+ label: zod_1.z.string().describe("Field label shown in UI"),
398
+ type: zod_1.z.enum([
399
+ 'activitylink', 'country', 'teams', 'text', 'textarea', 'textunit',
400
+ 'textpredefinedoptions', 'users', 'numeric', 'numericunit', 'date',
401
+ 'time', 'datetime', 'daterange', 'timerange', 'datetimerange', 'subheader'
402
+ ]).describe("Field type"),
403
+ key: zod_1.z.string().optional().describe("Readable field name (like SQL column name) - RECOMMENDED"),
404
+ required: zod_1.z.boolean().optional().describe("Whether field is required"),
405
+ data: zod_1.z.array(zod_1.z.string()).optional().describe("For activitylink: workflow IDs to link to. For textpredefinedoptions: allowed option values"),
406
+ placeholder: zod_1.z.string().optional().describe("Placeholder text"),
407
+ unit: zod_1.z.string().optional().describe("Unit for numeric/textunit fields"),
408
+ description: zod_1.z.string().optional().describe("Field description"),
409
+ editable: zod_1.z.boolean().optional().describe("Whether field is editable"),
410
+ }).passthrough())
411
+ .optional()
412
+ .refine((fields) => !fields || Object.keys(fields).every(id => /^_\d{4}$/.test(id)), { message: "Field IDs must match pattern _1000, _1001, etc. (underscore + 4 digits)" })
413
+ .describe("Field definitions keyed by field ID (_1000, _1001, etc.)"),
414
+ phases: zod_1.z
415
+ .record(zod_1.z.object({
416
+ name: zod_1.z.string().describe("Phase name"),
417
+ isInitial: zod_1.z.boolean().optional().describe("Whether activities can be created in this phase"),
418
+ fields: zod_1.z.array(zod_1.z.string()).optional().describe("Field IDs visible in this phase (defaults to all)"),
419
+ description: zod_1.z.string().optional().describe("Phase description"),
420
+ }).passthrough())
421
+ .optional()
422
+ .refine((phases) => !phases || Object.keys(phases).every(id => /^_\d{4}$/.test(id)), { message: "Phase IDs must match pattern _2000, _2001, etc. (underscore + 4 digits)" })
423
+ .describe("Phase definitions keyed by phase ID (_2000, _2001, etc.)"),
424
+ fieldsOrder: zod_1.z.array(zod_1.z.string()).optional().describe("Order of field IDs (auto-generated if omitted)"),
425
+ phasesOrder: zod_1.z.array(zod_1.z.string()).optional().describe("Order of phase IDs (auto-generated if omitted)"),
426
+ enableMessenger: zod_1.z.boolean().optional().describe("Enable discussion features (defaults to true)"),
427
+ enableLinkedAnnouncements: zod_1.z.boolean().optional().describe("Enable announcements (defaults to true)"),
428
+ defaultView: zod_1.z.enum(['timeline', 'table', 'kanban', 'calendar', 'map']).optional().describe("Default view type"),
429
+ }).passthrough())
430
+ .min(1, "At least one workflow template is required")
431
+ .max(100, "Maximum 100 workflow templates per installation")
432
+ .describe("Array of workflow template objects to install"),
433
+ });
434
+ exports.installWorkflowTool = {
435
+ name: 'install_workflow',
436
+ group: tool_registry_1.ToolGroup.PLAYGROUND,
437
+ description: installWorkflowDescription,
438
+ schema: installWorkflowSchema,
439
+ async execute(args, context) {
440
+ logger.debug('Installing workflow', {
441
+ templateCount: args.workflowTemplates.length,
442
+ apiKey: context.apiKey.substring(0, 8) + '...'
443
+ });
444
+ try {
445
+ // Get current workspace from API if not specified
446
+ let workspaceId = args.workspaceId;
447
+ if (!workspaceId) {
448
+ const initData = await context.hailer.request('v2.core.init', [["network"]]);
449
+ workspaceId = initData.network?._id;
450
+ }
451
+ if (!workspaceId) {
452
+ return {
453
+ content: [{
454
+ type: "text",
455
+ text: `āŒ Could not resolve workspace`,
456
+ }],
457
+ };
458
+ }
459
+ logger.debug('Calling v3.workflow.install', {
460
+ workspaceId,
461
+ templatesCount: args.workflowTemplates.length
462
+ });
463
+ // Call v3.workflow.install endpoint
464
+ const result = await context.hailer.request('v3.workflow.install', [
465
+ workspaceId,
466
+ args.workflowTemplates
467
+ ]);
468
+ logger.debug('Workflow installation successful', { result });
469
+ // Build success response
470
+ let responseText = `āœ… **Workflow Installation Successful**\n\n`;
471
+ responseText += `**Workspace:** ${workspaceId}\n`;
472
+ responseText += `**Workflows Installed:** ${args.workflowTemplates.length}\n\n`;
473
+ // Show workflow names and their real IDs
474
+ const templates = args.workflowTemplates;
475
+ const workflowMappings = templates
476
+ .filter((wf) => wf._id)
477
+ .map((wf) => `- ${wf.name}: \`${result[wf._id]}\` (from workflow _id \`${wf._id}\`)`)
478
+ .join('\n');
479
+ if (workflowMappings) {
480
+ responseText += `**Created Workflows:**\n${workflowMappings}\n\n`;
481
+ }
482
+ responseText += `**Complete ID Mapping (template_id → real_id):**\n`;
483
+ responseText += `\`\`\`json\n${JSON.stringify(result, null, 2)}\n\`\`\`\n\n`;
484
+ responseText += `šŸ’” **Next Steps:**\n`;
485
+ responseText += `- Use \`list_workflows\` to see the new workflows in context\n`;
486
+ responseText += `- Use \`create_activity\` with the real workflow IDs to create activities\n`;
487
+ responseText += `- Field IDs from mapping are used when setting activity field values\n\n`;
488
+ responseText += `**Example Activity Creation:**\n`;
489
+ responseText += `\`\`\`javascript\n`;
490
+ if (templates[0]?._id) {
491
+ const firstTemplate = templates[0];
492
+ const workflowId = result[firstTemplate._id];
493
+ const firstFieldId = Object.keys(firstTemplate.fields || {})[0];
494
+ const realFieldId = firstFieldId ? result[firstFieldId] : 'field_id';
495
+ responseText += `// Using workflow: ${firstTemplate.name}\n`;
496
+ responseText += `create_activity({\n`;
497
+ responseText += ` workflowId: "${workflowId}",\n`;
498
+ responseText += ` name: "New Activity",\n`;
499
+ responseText += ` fields: { "${realFieldId}": "value" }\n`;
500
+ responseText += `});\n`;
501
+ }
502
+ else {
503
+ responseText += `create_activity({\n`;
504
+ responseText += ` workflowId: result._0001,\n`;
505
+ responseText += ` name: "New Activity",\n`;
506
+ responseText += ` fields: { [result._1000]: "value" }\n`;
507
+ responseText += `});\n`;
508
+ }
509
+ responseText += `\`\`\``;
510
+ return {
511
+ content: [{
512
+ type: "text",
513
+ text: responseText,
514
+ }],
515
+ };
516
+ }
517
+ catch (error) {
518
+ const errorMessage = error instanceof Error ? error.message : String(error);
519
+ logger.error("Error installing workflow", new Error(errorMessage));
520
+ // Handle permission errors
521
+ if (errorMessage.includes('network admin') || errorMessage.includes('PermissionDenied')) {
522
+ return {
523
+ content: [{
524
+ type: "text",
525
+ text: `āŒ **Permission Denied**\n\nYou must be a workspace administrator to install workflows.\n\n**Error:** ${errorMessage}`,
526
+ }],
527
+ };
528
+ }
529
+ return {
530
+ content: [{
531
+ type: "text",
532
+ text: `āŒ **Error installing workflow**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- User must be workspace administrator\n- Templates must have valid structure (name, fields, phases)\n- Field/Phase IDs must match pattern _0001, _1000, _2000 (underscore + 4 digits)\n- Workspace ID must be valid`,
533
+ }],
534
+ };
535
+ }
536
+ }
537
+ };
538
+ // ============================================================================
539
+ // TOOL 2: REMOVE WORKFLOW
540
+ // ============================================================================
541
+ const removeWorkflowDescription = `🧪 [PLAYGROUND] Remove workflow - āš ļø DANGER: Permanently deletes workflow, activities, and discussions
542
+
543
+ āš ļø **MANDATORY: GATHER COMPLETE CONTEXT BEFORE CALLING THIS TOOL**
544
+ **BEFORE calling this tool, you are REQUIRED to:**
545
+ 1. Load the skill: \`get_skill({ skillName: "remove-workflow-skill" })\`
546
+ 2. Fetch workflow details with \`list_workflows\` to get:
547
+ - Workflow name and ID
548
+ - Workspace ID and name
549
+ - Activity count
550
+ - Relationships (which workflows link to this one)
551
+ 3. Show comprehensive confirmation message including:
552
+ - Workspace ID and name
553
+ - Workflow ID and name
554
+ - Exact count of activities that will be deleted
555
+ - Impact analysis - which other workflows will be affected (broken activity links)
556
+ - Clear irreversibility warning
557
+ 4. Wait for explicit user confirmation
558
+ **FAILURE TO GATHER AND SHOW THIS CONTEXT IS AN ERROR**
559
+
560
+ **Required**: workflowId
561
+ **Permission**: Workspace administrator`;
562
+ const removeWorkflowSchema = zod_1.z.object({
563
+ workflowId: zod_1.z
564
+ .string()
565
+ .min(1, "Workflow ID is required")
566
+ .describe("The workflow ID to remove (get from list_workflows)"),
567
+ workspaceId: zod_1.z
568
+ .string()
569
+ .optional()
570
+ .describe("Optional workspace ID or name - defaults to current workspace"),
571
+ confirmed: zod_1.z
572
+ .boolean()
573
+ .optional()
574
+ .describe("First confirmation - must be true to proceed"),
575
+ secondConfirmed: zod_1.z
576
+ .boolean()
577
+ .optional()
578
+ .describe("Second confirmation - must be true to proceed (required for double-check safety)"),
579
+ });
580
+ exports.removeWorkflowTool = {
581
+ name: 'remove_workflow',
582
+ group: tool_registry_1.ToolGroup.NUCLEAR,
583
+ description: removeWorkflowDescription,
584
+ schema: removeWorkflowSchema,
585
+ async execute(args, context) {
586
+ logger.debug('Removing workflow', {
587
+ workflowId: args.workflowId,
588
+ confirmed: args.confirmed,
589
+ secondConfirmed: args.secondConfirmed,
590
+ apiKey: context.apiKey.substring(0, 8) + '...'
591
+ });
592
+ try {
593
+ // Fetch workflow info from API to get name
594
+ const initData = await context.hailer.request('v2.core.init', [["processes", "network"]]);
595
+ const workflow = initData.processes?.find(p => p._id === args.workflowId);
596
+ if (!workflow) {
597
+ return {
598
+ content: [{
599
+ type: "text",
600
+ text: `āŒ Workflow "${args.workflowId}" not found`,
601
+ }],
602
+ };
603
+ }
604
+ const workflowName = workflow.name;
605
+ const workspaceId = args.workspaceId || initData.network?._id;
606
+ const workspaceName = initData.network?.name || 'Unknown';
607
+ // SAFETY CHECK: Require double confirmation
608
+ if (!args.confirmed || !args.secondConfirmed) {
609
+ let warningText = `āš ļø **DESTRUCTIVE OPERATION - CONFIRMATION REQUIRED**\n\n`;
610
+ warningText += `You are about to **permanently delete** the following:\n\n`;
611
+ warningText += `**Workflow:** ${workflowName}\n`;
612
+ warningText += `**Workflow ID:** \`${args.workflowId}\`\n`;
613
+ warningText += `**Workspace:** ${workspaceName} (\`${workspaceId}\`)\n\n`;
614
+ warningText += `**āš ļø This will permanently delete:**\n`;
615
+ warningText += `- Workflow structure (fields, phases)\n`;
616
+ warningText += `- ALL activities/records in this workflow\n`;
617
+ warningText += `- ALL discussions and messages\n`;
618
+ warningText += `- ALL relationships (ActivityLinks)\n\n`;
619
+ warningText += `**🚨 THIS CANNOT BE UNDONE! 🚨**\n\n`;
620
+ warningText += `**To proceed, you must:**\n`;
621
+ warningText += `1. Review the \`remove-workflow-skill\` (REQUIRED)\n`;
622
+ warningText += `2. Call this tool again with BOTH confirmations:\n\n`;
623
+ warningText += `\`\`\`javascript\n`;
624
+ warningText += `remove_workflow({\n`;
625
+ warningText += ` workflowId: "${args.workflowId}",\n`;
626
+ warningText += ` confirmed: true,\n`;
627
+ warningText += ` secondConfirmed: true\n`;
628
+ warningText += `})\n`;
629
+ warningText += `\`\`\`\n\n`;
630
+ warningText += `šŸ’” **Before proceeding:**\n`;
631
+ warningText += `- Load \`remove-workflow-skill\` to review safety checklist\n`;
632
+ warningText += `- Export data if needed (\`list_activities\`)\n`;
633
+ warningText += `- Check for dependencies (\`list_workflows\`)\n`;
634
+ warningText += `- Verify with user that this is intentional`;
635
+ return {
636
+ content: [{
637
+ type: "text",
638
+ text: warningText,
639
+ }],
640
+ };
641
+ }
642
+ logger.debug('Calling process.remove', {
643
+ workflowId: args.workflowId,
644
+ workflowName,
645
+ workspaceId,
646
+ workspaceName
647
+ });
648
+ // Call process.remove endpoint
649
+ await context.hailer.request('process.remove', [args.workflowId]);
650
+ logger.debug('Workflow removal successful', {
651
+ workflowId: args.workflowId,
652
+ workflowName,
653
+ workspaceId,
654
+ workspaceName
655
+ });
656
+ // Build success response
657
+ let responseText = `āœ… **Workflow Removed Successfully**\n\n`;
658
+ responseText += `**Workflow:** ${workflowName}\n`;
659
+ responseText += `**Workflow ID:** \`${args.workflowId}\`\n`;
660
+ responseText += `**Workspace:** ${workspaceName} (\`${workspaceId}\`)\n\n`;
661
+ responseText += `āš ļø **All activities and discussions in this workflow have been permanently deleted.**\n\n`;
662
+ responseText += `šŸ’” Use \`list_workflows\` to see remaining workflows in the workspace.`;
663
+ return {
664
+ content: [{
665
+ type: "text",
666
+ text: responseText,
667
+ }],
668
+ };
669
+ }
670
+ catch (error) {
671
+ logger.error("Error removing workflow", error);
672
+ const errorMessage = error instanceof Error ? error.message : String(error);
673
+ // Handle permission errors
674
+ if (errorMessage.includes('network admin') || errorMessage.includes('PermissionDenied') || errorMessage.includes('permission')) {
675
+ return {
676
+ content: [{
677
+ type: "text",
678
+ text: `āŒ **Permission Denied**\n\nYou must be a workspace administrator to remove workflows.\n\n**Error:** ${errorMessage}`,
679
+ }],
680
+ };
681
+ }
682
+ // Handle not found errors
683
+ if (errorMessage.includes('not found') || errorMessage.includes('NotFound')) {
684
+ return {
685
+ content: [{
686
+ type: "text",
687
+ text: `āŒ **Workflow Not Found**\n\nThe specified workflow does not exist or you don't have access to it.\n\n**Workflow ID:** \`${args.workflowId}\`\n\n**Error:** ${errorMessage}\n\nšŸ’” Use \`list_workflows\` to see available workflows.`,
688
+ }],
689
+ };
690
+ }
691
+ return {
692
+ content: [{
693
+ type: "text",
694
+ text: `āŒ **Error removing workflow**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- User must be workspace administrator\n- Workflow ID must be valid and exist in the workspace\n- Workflow must not be referenced by other workflows (check ActivityLinks)`,
695
+ }],
696
+ };
697
+ }
698
+ }
699
+ };
700
+ // ============================================================================
701
+ // TOOL 3: UPDATE WORKFLOW FIELD
702
+ // ============================================================================
703
+ const updateWorkflowFieldDescription = `🧪 [PLAYGROUND] Update workflow field - Modify field properties and configure function fields
704
+
705
+ āš ļø **MANDATORY: YOU MUST LOAD SKILL FIRST**
706
+ **BEFORE calling this tool, you are REQUIRED to:**
707
+ 1. Load the skill: \`get_skill({ skillName: "update-workflow-field-skill" })\`
708
+ 2. Read the examples and best practices
709
+ 3. Understand function fields and field type restrictions
710
+ **FAILURE TO LOAD THE SKILL FIRST IS AN ERROR**
711
+
712
+ **Required Parameters**:
713
+ - workflowId: Workflow ID
714
+ - fieldId: Field ID to update
715
+ - fieldData: Properties to update (label, key, type, data, required, function, functionVariables, functionEnabled)
716
+
717
+ **IMPORTANT - Updating Labels Only:**
718
+ When updating ONLY the label (not the key), set \`autoGenerateKey: false\` to prevent key regeneration:
719
+ - āœ… Correct: \`{ fieldData: { label: "Nationality" }, autoGenerateKey: false }\`
720
+ - āŒ Wrong: \`{ fieldData: { label: "Nationality" } }\` (will try to regenerate key and may fail)
721
+
722
+ **Auto-generate key**: When creating NEW keys, set label only and key auto-generates as camelCase ("Start Date" → "startDate")
723
+
724
+ **Field Type Changes**: Avoid changing field types (text → country, etc.) as this may be restricted by the API
725
+
726
+ **Use Cases**: Add keys, update labels, update activitylink targets, configure calculated fields, modify options
727
+
728
+ **Examples**:
729
+ - Update label only: \`update_workflow_field({ workflowId: "xxx", fieldId: "yyy", fieldData: { label: "New Label" }, autoGenerateKey: false })\`
730
+ - Update label and options: \`update_workflow_field({ workflowId: "xxx", fieldId: "yyy", fieldData: { label: "Status", data: ["Active", "Inactive"] }, autoGenerateKey: false })\`
731
+ - Add new key to field: \`update_workflow_field({ workflowId: "xxx", fieldId: "yyy", fieldData: { label: "My Field" }, autoGenerateKey: true })\` (generates key: "myField")`;
732
+ const updateWorkflowFieldSchema = zod_1.z.object({
733
+ workflowId: zod_1.z
734
+ .string()
735
+ .min(1, "Workflow ID is required")
736
+ .describe("The workflow ID containing the field (get from list_workflows)"),
737
+ fieldId: zod_1.z
738
+ .string()
739
+ .min(1, "Field ID is required")
740
+ .describe("The field ID to update (get from workflow schema)"),
741
+ fieldData: zod_1.z
742
+ .record(zod_1.z.unknown())
743
+ .describe("Field properties to update (label, key, type, data, required, etc.)"),
744
+ workspaceId: zod_1.z
745
+ .string()
746
+ .optional()
747
+ .describe("Optional workspace ID or name - defaults to current workspace"),
748
+ language: zod_1.z
749
+ .string()
750
+ .default("en")
751
+ .optional()
752
+ .describe("Language code for the update (default: 'en')"),
753
+ autoGenerateKey: zod_1.z
754
+ .boolean()
755
+ .default(true)
756
+ .optional()
757
+ .describe("Auto-generate camelCase key from label if key not provided (default: true). IMPORTANT: Set to false when updating ONLY the label to prevent key regeneration errors."),
758
+ });
759
+ exports.updateWorkflowFieldTool = {
760
+ name: 'update_workflow_field',
761
+ group: tool_registry_1.ToolGroup.PLAYGROUND,
762
+ description: updateWorkflowFieldDescription,
763
+ schema: updateWorkflowFieldSchema,
764
+ async execute(args, context) {
765
+ logger.debug('Updating workflow field', {
766
+ workflowId: args.workflowId,
767
+ fieldId: args.fieldId,
768
+ apiKey: context.apiKey.substring(0, 8) + '...'
769
+ });
770
+ try {
771
+ // Get current workspace from API if not specified
772
+ let workspaceId = args.workspaceId;
773
+ if (!workspaceId) {
774
+ const initData = await context.hailer.request('v2.core.init', [["network"]]);
775
+ workspaceId = initData.network?._id;
776
+ }
777
+ // Process field data - auto-generate key from label if requested
778
+ const fieldData = { ...args.fieldData };
779
+ const autoGenerateKey = args.autoGenerateKey !== false; // Default to true
780
+ if (autoGenerateKey && fieldData.label && !fieldData.key) {
781
+ const generatedKey = labelToCamelCase(fieldData.label);
782
+ fieldData.key = generatedKey;
783
+ logger.debug('Auto-generated key from label', {
784
+ label: fieldData.label,
785
+ key: generatedKey
786
+ });
787
+ }
788
+ const language = args.language || 'en';
789
+ logger.debug('Calling process.update_field', {
790
+ workflowId: args.workflowId,
791
+ fieldId: args.fieldId,
792
+ language,
793
+ fieldData
794
+ });
795
+ // Call process.update_field endpoint
796
+ await context.hailer.request('process.update_field', [
797
+ args.workflowId,
798
+ args.fieldId,
799
+ fieldData,
800
+ language
801
+ ]);
802
+ logger.debug('Workflow field update successful', {
803
+ workflowId: args.workflowId,
804
+ fieldId: args.fieldId
805
+ });
806
+ // Build success response
807
+ let responseText = `āœ… **Workflow Field Updated Successfully**\n\n`;
808
+ responseText += `**Workflow ID:** \`${args.workflowId}\`\n`;
809
+ responseText += `**Field ID:** \`${args.fieldId}\`\n`;
810
+ responseText += `**Workspace:** ${workspaceId}\n\n`;
811
+ responseText += `**Updated Properties:**\n`;
812
+ Object.entries(fieldData).forEach(([key, value]) => {
813
+ const valueStr = typeof value === 'object' ? JSON.stringify(value) : String(value);
814
+ responseText += `- ${key}: \`${valueStr}\`\n`;
815
+ });
816
+ responseText += `\nšŸ’” **Next Steps:**\n`;
817
+ responseText += `- Use \`get_workflow_schema\` to verify the field changes\n`;
818
+ responseText += `- Use \`list_activities\` with \`returnFlat: true\` to see the key in action\n`;
819
+ if (fieldData.key) {
820
+ responseText += `\n**Using the new key:**\n`;
821
+ responseText += `When creating/updating activities with \`returnFlat: true\`, use:\n`;
822
+ responseText += `\`\`\`javascript\n`;
823
+ responseText += `fields: { "${fieldData.key}": "value" }\n`;
824
+ responseText += `\`\`\``;
825
+ }
826
+ return {
827
+ content: [{
828
+ type: "text",
829
+ text: responseText,
830
+ }],
831
+ };
832
+ }
833
+ catch (error) {
834
+ logger.error("Error updating workflow field", error);
835
+ const errorMessage = error instanceof Error ? error.message : String(error);
836
+ // Handle permission errors
837
+ if (errorMessage.includes('network admin') || errorMessage.includes('PermissionDenied') || errorMessage.includes('permission')) {
838
+ return {
839
+ content: [{
840
+ type: "text",
841
+ text: `āŒ **Permission Denied**\n\nYou must be a workspace administrator to update workflow fields.\n\n**Error:** ${errorMessage}`,
842
+ }],
843
+ };
844
+ }
845
+ // Handle not found errors
846
+ if (errorMessage.includes('not found') || errorMessage.includes('NotFound')) {
847
+ return {
848
+ content: [{
849
+ type: "text",
850
+ text: `āŒ **Workflow or Field Not Found**\n\nThe specified workflow or field does not exist.\n\n**Workflow ID:** \`${args.workflowId}\`\n**Field ID:** \`${args.fieldId}\`\n\n**Error:** ${errorMessage}\n\nšŸ’” Use \`list_workflows\` and \`get_workflow_schema\` to see available workflows and fields.`,
851
+ }],
852
+ };
853
+ }
854
+ return {
855
+ content: [{
856
+ type: "text",
857
+ text: `āŒ **Error updating workflow field**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- User must be workspace administrator\n- Workflow ID and Field ID must be valid\n- Field data must match field type requirements\n- Some field types may have restrictions on updates`,
858
+ }],
859
+ };
860
+ }
861
+ }
862
+ };
863
+ // ============================================================================
864
+ // TOOL 4: TEST FUNCTION FIELD
865
+ // ============================================================================
866
+ const testFunctionFieldDescription = `🧪 [PLAYGROUND] Test function field - Dry-run test function code against real activity before saving
867
+
868
+ **Required**: activityId, fieldId, function (JavaScript accessing dep.variableName)
869
+ **Returns**: Result value, resolved dependencies, execution stats, or detailed errors
870
+ **Workflow**: Configure with update_workflow_field → test → fix → re-test → deploy`;
871
+ const testFunctionFieldSchema = zod_1.z.object({
872
+ activityId: zod_1.z
873
+ .string()
874
+ .min(1, "Activity ID is required")
875
+ .describe("Activity ID to test the function against (must have dependency field values)"),
876
+ fieldId: zod_1.z
877
+ .string()
878
+ .min(1, "Field ID is required")
879
+ .describe("Field ID for context and to get field configuration (label, type, etc.)"),
880
+ function: zod_1.z
881
+ .string()
882
+ .min(1, "Function code is required")
883
+ .describe("JavaScript function code to test - access dependencies via dep.variableName"),
884
+ functionVariables: zod_1.z
885
+ .record(zod_1.z.object({
886
+ type: zod_1.z.string().describe("Dependency type - use '=' for same-activity field"),
887
+ data: zod_1.z.array(zod_1.z.string()).describe("Array with field ID to depend on")
888
+ }))
889
+ .optional()
890
+ .describe("Optional function dependencies - if not provided, reads from field definition"),
891
+ });
892
+ exports.testFunctionFieldTool = {
893
+ name: 'test_function_field',
894
+ group: tool_registry_1.ToolGroup.PLAYGROUND,
895
+ description: testFunctionFieldDescription,
896
+ schema: testFunctionFieldSchema,
897
+ async execute(args, context) {
898
+ logger.debug('Testing function field', {
899
+ activityId: args.activityId,
900
+ fieldId: args.fieldId
901
+ });
902
+ try {
903
+ // Fetch activity to get workflow context
904
+ const activity = await context.hailer.fetchActivityById(args.activityId);
905
+ if (!activity?.process) {
906
+ return {
907
+ content: [{
908
+ type: "text",
909
+ text: `āŒ Could not find activity or workflow for activity ID: ${args.activityId}`
910
+ }],
911
+ };
912
+ }
913
+ // Get workflow field definition from API
914
+ const initData = await context.hailer.request('v2.core.init', [["processes"]]);
915
+ const workflow = initData.processes?.find(p => p._id === activity.process);
916
+ const field = workflow?.fields?.[args.fieldId];
917
+ if (!field) {
918
+ return {
919
+ content: [{
920
+ type: "text",
921
+ text: `āŒ Field not found in workflow.
922
+
923
+ **Field ID:** \`${args.fieldId}\`
924
+ **Workflow ID:** \`${activity.process}\`
925
+
926
+ šŸ’” Use \`get_workflow_schema\` to see available fields.`,
927
+ }],
928
+ };
929
+ }
930
+ // Resolve function variables (use provided or field defaults)
931
+ const functionVariables = args.functionVariables || field.functionVariables;
932
+ if (!functionVariables) {
933
+ return {
934
+ content: [{
935
+ type: "text",
936
+ text: `āŒ No function dependencies provided.
937
+
938
+ **Field:** ${field.label || args.fieldId}
939
+
940
+ You must provide \`functionVariables\` parameter, or the field must have dependencies configured.
941
+
942
+ **Example:**
943
+ \`\`\`javascript
944
+ functionVariables: {
945
+ "likelihood": { "type": "=", "data": ["field-id-1"] },
946
+ "impact": { "type": "=", "data": ["field-id-2"] }
947
+ }
948
+ \`\`\``,
949
+ }],
950
+ };
951
+ }
952
+ // Evaluate the function code
953
+ const result = await context.hailer.request('v2.activities.evaluate', [
954
+ args.activityId,
955
+ args.fieldId,
956
+ args.function,
957
+ functionVariables
958
+ ]);
959
+ // Check for JavaScript errors in the evaluation
960
+ const errorMessage = extractEvaluationError(result, args.fieldId);
961
+ if (errorMessage) {
962
+ const errorType = parseErrorType(errorMessage);
963
+ const cleanMessage = errorMessage.replace(/^(SyntaxError|ReferenceError|TypeError):\s*/, '');
964
+ return {
965
+ content: [{
966
+ type: "text",
967
+ text: `āŒ **Function Field Test Failed**
968
+
969
+ **Error Type:** ${errorType}
970
+ **Error Message:** ${cleanMessage}
971
+
972
+ šŸ’” **Common Issues:**
973
+ - **SyntaxError**: Missing brackets, semicolons, or quotes in JavaScript code
974
+ - **ReferenceError**: Variable not defined or typo in dependency name (\`dep.variableName\`)
975
+ - **TypeError**: Invalid operation on wrong data type
976
+
977
+ **Next Steps:**
978
+ 1. Fix the function code syntax/logic
979
+ 2. Test again with \`test_function_field\`
980
+ 3. When test passes, save with \`update_workflow_field\``,
981
+ }],
982
+ };
983
+ }
984
+ // Build success response
985
+ const evaluatedField = result?.activity?.fields?.find((f) => f.id === args.fieldId);
986
+ const calculatedValue = evaluatedField?.value;
987
+ const dependencies = result?.context?.dependencies || {};
988
+ const stats = result?.context?.stats || {};
989
+ let text = `āœ… **Function Field Test Successful**
990
+
991
+ **Field:** ${field.label || args.fieldId} (${field.type || 'unknown'})
992
+ **Activity:** ${activity.name || activity._id}
993
+
994
+ **šŸ“Š Calculated Result:** \`${calculatedValue !== undefined ? calculatedValue : 'undefined'}\``;
995
+ if (field.type === 'numeric' && typeof calculatedValue === 'number' && field.unit) {
996
+ text += ` (${calculatedValue} ${field.unit})`;
997
+ }
998
+ text += '\n';
999
+ // Show resolved dependencies
1000
+ if (Object.keys(dependencies).length > 0) {
1001
+ text += '\n**šŸ”— Dependencies Resolved:**\n';
1002
+ Object.entries(dependencies).forEach(([varName, value]) => {
1003
+ text += `- ${varName}: \`${value}\`\n`;
1004
+ });
1005
+ }
1006
+ // Show execution stats
1007
+ const executionTime = stats['Executing Sandbox (ms) (It:0/Ops:1)'];
1008
+ const iterations = stats['Iterations Used'];
1009
+ if (executionTime !== undefined || iterations !== undefined) {
1010
+ text += '\n**ā±ļø Execution Statistics:**\n';
1011
+ if (executionTime !== undefined)
1012
+ text += `- Execution Time: ${executionTime}ms\n`;
1013
+ if (iterations !== undefined)
1014
+ text += `- Iterations: ${iterations}\n`;
1015
+ }
1016
+ text += `
1017
+ **āœ… Next Steps:**
1018
+ - Test with other activities to verify edge cases
1019
+ - Check activities where dependencies might be null/undefined
1020
+ - If you need to modify the function, use \`update_workflow_field\` to save changes
1021
+ - Function is working correctly and ready for production use!`;
1022
+ return {
1023
+ content: [{ type: "text", text }],
1024
+ };
1025
+ }
1026
+ catch (error) {
1027
+ logger.error("Error testing function field", error);
1028
+ const errorMessage = error instanceof Error
1029
+ ? error.message
1030
+ : JSON.stringify(error, null, 2);
1031
+ return {
1032
+ content: [{
1033
+ type: "text",
1034
+ text: `āŒ **Error Testing Function Field**
1035
+
1036
+ ${errorMessage}
1037
+
1038
+ šŸ’” This might indicate:
1039
+ - API connection issue
1040
+ - Invalid activity or field ID
1041
+ - Backend processing error`,
1042
+ }],
1043
+ };
1044
+ }
1045
+ }
1046
+ };
1047
+ // ============================================================================
1048
+ // TOOL 5: LIST WORKFLOWS MINIMAL
1049
+ // ============================================================================
1050
+ const listWorkflowsMinimalDescription = `🧪 [PLAYGROUND] List workflows with minimal output - Returns only id and name to avoid token limits`;
1051
+ const listWorkflowsMinimalSchema = zod_1.z.object({
1052
+ workspace: zod_1.z.string().optional().describe("Optional workspace ID or name"),
1053
+ limit: zod_1.z.coerce.number().optional().describe("Maximum number of workflows to return"),
1054
+ offset: zod_1.z.coerce.number().optional().default(0).describe("Number of workflows to skip (for pagination)"),
1055
+ search: zod_1.z.string().optional().describe("Filter workflows by name (case-insensitive partial match)"),
1056
+ starredOnly: zod_1.z.coerce.boolean().optional().default(false).describe("Only return starred workflows"),
1057
+ });
1058
+ exports.listWorkflowsMinimalTool = {
1059
+ name: 'list_workflows_minimal',
1060
+ group: tool_registry_1.ToolGroup.PLAYGROUND,
1061
+ description: listWorkflowsMinimalDescription,
1062
+ schema: listWorkflowsMinimalSchema,
1063
+ async execute(args, context) {
1064
+ logger.debug('Listing workflows (minimal)', {
1065
+ apiKey: context.apiKey.substring(0, 8) + '...'
1066
+ });
1067
+ try {
1068
+ // Fetch workflows directly from API
1069
+ const initData = await context.hailer.request('v2.core.init', [["processes"]]);
1070
+ let workflows = initData.processes || [];
1071
+ // Filter by workspace if specified
1072
+ if (args.workspace) {
1073
+ workflows = workflows.filter(w => w.cid === args.workspace);
1074
+ }
1075
+ // Filter by starred if requested
1076
+ if (args.starredOnly) {
1077
+ workflows = workflows.filter(w => w.isStarred);
1078
+ }
1079
+ // Search by name if provided
1080
+ if (args.search) {
1081
+ const searchLower = args.search.toLowerCase();
1082
+ workflows = workflows.filter(w => w.name.toLowerCase().includes(searchLower));
1083
+ }
1084
+ // Apply pagination
1085
+ const offset = args.offset || 0;
1086
+ const limit = args.limit;
1087
+ const totalCount = workflows.length;
1088
+ if (limit) {
1089
+ workflows = workflows.slice(offset, offset + limit);
1090
+ }
1091
+ else if (offset > 0) {
1092
+ workflows = workflows.slice(offset);
1093
+ }
1094
+ // Build minimal response - only id and name
1095
+ const minimalWorkflows = workflows.map(w => ({
1096
+ id: w._id,
1097
+ name: w.name,
1098
+ workspaceId: w.cid,
1099
+ activityCount: w.createdActivities || 0,
1100
+ isStarred: w.isStarred || false
1101
+ }));
1102
+ let responseText = `šŸ“‹ **Workflows Found**\n\n`;
1103
+ responseText += `Total: ${totalCount}\n`;
1104
+ responseText += `Showing: ${workflows.length}\n`;
1105
+ if (offset > 0)
1106
+ responseText += `Offset: ${offset}\n`;
1107
+ responseText += `\n`;
1108
+ minimalWorkflows.forEach(w => {
1109
+ const star = w.isStarred ? '⭐ ' : '';
1110
+ responseText += `${star}**${w.name}**\n`;
1111
+ responseText += ` ID: \`${w.id}\`\n`;
1112
+ responseText += ` Activities: ${w.activityCount}\n\n`;
1113
+ });
1114
+ responseText += `\nšŸ’” **Next Steps:**\n`;
1115
+ responseText += `- Use \`get_workflow_schema\` with workflow ID to see fields\n`;
1116
+ responseText += `- Use \`list_workflow_phases\` to see available phases\n`;
1117
+ if (limit && (offset + limit) < totalCount) {
1118
+ responseText += `- Use \`offset: ${offset + limit}\` to see next page\n`;
1119
+ }
1120
+ return {
1121
+ content: [{
1122
+ type: "text",
1123
+ text: responseText,
1124
+ }],
1125
+ };
1126
+ }
1127
+ catch (error) {
1128
+ logger.error("Error listing workflows (minimal)", error);
1129
+ return {
1130
+ content: [{
1131
+ type: "text",
1132
+ text: `āŒ Error listing workflows: ${error instanceof Error ? error.message : String(error)}`,
1133
+ }],
1134
+ };
1135
+ }
1136
+ }
1137
+ };
1138
+ // ============================================================================
1139
+ // COUNT ACTIVITIES TOOL
1140
+ // ============================================================================
1141
+ const countActivitiesDescription = `šŸ”¢ Count Activities - Get total count of activities in a workflow
1142
+
1143
+ **Purpose**: Returns the total number of activities in a specific workflow. Useful for analytics, reporting, and understanding workflow size.
1144
+
1145
+ **Example**:
1146
+ \`\`\`javascript
1147
+ count_activities({
1148
+ workflowId: "691b136906e6923eac972902"
1149
+ })
1150
+ \`\`\`
1151
+
1152
+ **Response**: Returns count in details object
1153
+
1154
+ **Use Cases**:
1155
+ - Analytics and reporting
1156
+ - Workflow size estimation
1157
+ - Data migration planning
1158
+ - Performance monitoring
1159
+
1160
+ **Tips**:
1161
+ - Much faster than list_activities for just getting count
1162
+ - Use list_workflows to get workflow IDs`;
1163
+ const countActivitiesSchema = zod_1.z.object({
1164
+ workflowId: zod_1.z.string().describe("Workflow ID to count activities from (24 characters)"),
1165
+ });
1166
+ exports.countActivitiesTool = {
1167
+ name: 'count_activities',
1168
+ group: tool_registry_1.ToolGroup.PLAYGROUND,
1169
+ description: countActivitiesDescription,
1170
+ schema: countActivitiesSchema,
1171
+ async execute(args, context) {
1172
+ logger.debug('Counting activities', {
1173
+ workflowId: args.workflowId,
1174
+ apiKey: context.apiKey.substring(0, 8) + '...'
1175
+ });
1176
+ try {
1177
+ // Get workflow name for better response
1178
+ const initData = await context.hailer.request('v2.core.init', [["processes"]]);
1179
+ const workflow = initData.processes?.find(p => p._id === args.workflowId);
1180
+ if (!workflow) {
1181
+ return {
1182
+ content: [{
1183
+ type: "text",
1184
+ text: `āŒ Workflow "${args.workflowId}" not found`,
1185
+ }],
1186
+ };
1187
+ }
1188
+ logger.debug('Calling v3.activity.count', {
1189
+ workflowId: args.workflowId,
1190
+ workflowName: workflow.name
1191
+ });
1192
+ // Call v3.activity.count endpoint
1193
+ // Returns object with phase IDs as keys and counts as values
1194
+ const result = await context.hailer.request('v3.activity.count', [args.workflowId]);
1195
+ logger.debug('Activity count retrieved', {
1196
+ result: JSON.stringify(result)
1197
+ });
1198
+ // Sum all counts across phases
1199
+ const count = Object.values(result).reduce((sum, phaseCount) => sum + phaseCount, 0);
1200
+ // Build success response
1201
+ let responseText = `šŸ”¢ **Activity Count for "${workflow.name}"**\n\n`;
1202
+ responseText += `**Workflow ID:** \`${args.workflowId}\`\n`;
1203
+ responseText += `**Total Activities:** ${count}\n\n`;
1204
+ if (count === 0) {
1205
+ responseText += `šŸ’” This workflow has no activities yet. Use \`create_activity\` to add one.\n`;
1206
+ }
1207
+ else {
1208
+ responseText += `šŸ’” **Next Steps:**\n`;
1209
+ responseText += `- Use \`list_activities\` to see the activities\n`;
1210
+ responseText += `- Use \`get_workflow_schema\` to see workflow structure\n`;
1211
+ }
1212
+ return {
1213
+ content: [{
1214
+ type: "text",
1215
+ text: responseText,
1216
+ }],
1217
+ };
1218
+ }
1219
+ catch (error) {
1220
+ logger.error("Error counting activities", error);
1221
+ return {
1222
+ content: [{
1223
+ type: "text",
1224
+ text: `āŒ Error counting activities: ${error instanceof Error ? error.message : String(error)}\n\n**Tips:**\n- Check that workflow ID is valid (24 characters)\n- Use \`list_workflows\` to find workflow IDs`,
1225
+ }],
1226
+ };
1227
+ }
1228
+ }
1229
+ };
1230
+ // ============================================================================
1231
+ // UPDATE WORKFLOW PHASE
1232
+ // ============================================================================
1233
+ const updateWorkflowPhaseDescription = `šŸ·ļø Update Workflow Phase - Modify phase name and properties
1234
+
1235
+ **What does this do?**
1236
+ Updates an existing workflow phase's properties like name, description, and settings.
1237
+
1238
+ **Required Parameters**:
1239
+ - workflowId: Workflow ID containing the phase
1240
+ - phaseId: Phase ID to update
1241
+ - phaseData: Properties to update (name, description, etc.)
1242
+
1243
+ **Example - Rename phase**:
1244
+ \`\`\`javascript
1245
+ update_workflow_phase({
1246
+ workflowId: "68446dc05b30685f67c6fcd4",
1247
+ phaseId: "181",
1248
+ phaseData: { name: "Injured" }
1249
+ })
1250
+ \`\`\`
1251
+
1252
+ **Updatable Properties**:
1253
+ - name - Phase display name
1254
+ - description - Phase description
1255
+ - color - Phase color code
1256
+
1257
+ **Requirements**:
1258
+ - User must be workspace administrator
1259
+ `;
1260
+ const updateWorkflowPhaseSchema = zod_1.z.object({
1261
+ workflowId: zod_1.z.string().describe("The workflow ID containing the phase (24 characters)"),
1262
+ phaseId: zod_1.z.string().describe("The phase ID to update"),
1263
+ phaseData: zod_1.z.object({
1264
+ name: zod_1.z.string().optional().describe("New phase name"),
1265
+ description: zod_1.z.string().optional().describe("New phase description"),
1266
+ color: zod_1.z.string().optional().describe("Phase color code"),
1267
+ }).passthrough().describe("Phase properties to update"),
1268
+ workspaceId: zod_1.z.string().optional().describe("Optional workspace ID - defaults to current workspace"),
1269
+ });
1270
+ exports.updateWorkflowPhaseTool = {
1271
+ name: 'update_workflow_phase',
1272
+ group: tool_registry_1.ToolGroup.PLAYGROUND,
1273
+ description: updateWorkflowPhaseDescription,
1274
+ schema: updateWorkflowPhaseSchema,
1275
+ async execute(args, context) {
1276
+ logger.debug('Updating workflow phase', {
1277
+ workflowId: args.workflowId,
1278
+ phaseId: args.phaseId,
1279
+ apiKey: context.apiKey.substring(0, 8) + '...'
1280
+ });
1281
+ try {
1282
+ // Get current workspace from API if not specified
1283
+ let workspaceId = args.workspaceId;
1284
+ if (!workspaceId) {
1285
+ const initData = await context.hailer.request('v2.core.init', [["network"]]);
1286
+ workspaceId = initData.network?._id;
1287
+ }
1288
+ const phaseData = args.phaseData;
1289
+ const language = 'en'; // Default language
1290
+ logger.debug('Calling REST /phase/update', {
1291
+ workflowId: args.workflowId,
1292
+ phaseId: args.phaseId,
1293
+ phaseData
1294
+ });
1295
+ // Call REST endpoint /api/phase/update
1296
+ // API format: {"0": workflowId, "1": phaseId, "2": phaseSettings, "3": language}
1297
+ await context.hailer.callRest({
1298
+ operation: 'update_workflow_phase',
1299
+ endpoint: '/api/phase/update',
1300
+ method: 'POST',
1301
+ body: {
1302
+ "0": args.workflowId,
1303
+ "1": args.phaseId,
1304
+ "2": phaseData,
1305
+ "3": language
1306
+ }
1307
+ });
1308
+ logger.debug('Workflow phase update successful', {
1309
+ workflowId: args.workflowId,
1310
+ phaseId: args.phaseId
1311
+ });
1312
+ // Build success response
1313
+ let responseText = `āœ… **Workflow Phase Updated Successfully**\n\n`;
1314
+ responseText += `**Workflow ID:** \`${args.workflowId}\`\n`;
1315
+ responseText += `**Phase ID:** \`${args.phaseId}\`\n`;
1316
+ responseText += `**Workspace:** ${workspaceId}\n\n`;
1317
+ responseText += `**Updated Properties:**\n`;
1318
+ Object.entries(phaseData).forEach(([key, value]) => {
1319
+ const valueStr = typeof value === 'object' ? JSON.stringify(value) : String(value);
1320
+ responseText += `- ${key}: \`${valueStr}\`\n`;
1321
+ });
1322
+ responseText += `\nšŸ’” **Next Steps:**\n`;
1323
+ responseText += `- Use \`list_workflow_phases\` to verify the phase changes\n`;
1324
+ responseText += `- Phase changes will be immediately visible in the workflow\n`;
1325
+ return {
1326
+ content: [{
1327
+ type: "text",
1328
+ text: responseText,
1329
+ }],
1330
+ };
1331
+ }
1332
+ catch (error) {
1333
+ logger.error("Error updating workflow phase", error);
1334
+ const errorMessage = error instanceof Error ? error.message : String(error);
1335
+ // Handle permission errors
1336
+ if (errorMessage.includes('network admin') || errorMessage.includes('PermissionDenied') || errorMessage.includes('permission')) {
1337
+ return {
1338
+ content: [{
1339
+ type: "text",
1340
+ text: `āŒ **Permission Denied**\n\nYou must be a workspace administrator to update workflow phases.\n\n**Error:** ${errorMessage}`,
1341
+ }],
1342
+ };
1343
+ }
1344
+ // Handle method not found errors
1345
+ if (errorMessage.includes('Method not found')) {
1346
+ return {
1347
+ content: [{
1348
+ type: "text",
1349
+ text: `āŒ **API Method Not Available**\n\nThe \`process.update_phase\` API method may not be available in this Hailer version.\n\n**Alternative:** Use the Hailer web interface to update phase names manually.\n\n**Error:** ${errorMessage}`,
1350
+ }],
1351
+ };
1352
+ }
1353
+ return {
1354
+ content: [{
1355
+ type: "text",
1356
+ text: `āŒ **Error updating workflow phase**\n\n**Error:** ${errorMessage}\n\n**Common Issues:**\n- User must be workspace administrator\n- Workflow ID must be valid (24 characters)\n- Phase ID must exist in the workflow\n- Use \`list_workflow_phases\` to find phase IDs`,
1357
+ }],
1358
+ };
1359
+ }
1360
+ }
1361
+ };
1362
+ //# sourceMappingURL=workflow.js.map