@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.
- package/.claude/commands/tool-builder.md +37 -0
- package/.claude/commands/ws-pull.md +44 -0
- package/.claude/settings.json +8 -0
- package/.claude/settings.local.json +49 -0
- package/.claude/skills/activity-api/SKILL.md +96 -0
- package/.claude/skills/activity-api/references/activity-endpoints.md +845 -0
- package/.claude/skills/add-app-member-skill/SKILL.md +977 -0
- package/.claude/skills/agent-building/SKILL.md +243 -0
- package/.claude/skills/agent-building/references/architecture-patterns.md +446 -0
- package/.claude/skills/agent-building/references/code-examples.md +587 -0
- package/.claude/skills/agent-building/references/implementation-guide.md +619 -0
- package/.claude/skills/app-api/SKILL.md +219 -0
- package/.claude/skills/app-api/references/app-endpoints.md +759 -0
- package/.claude/skills/building-hailer-apps-skill/SKILL.md +548 -0
- package/.claude/skills/create-app-skill/SKILL.md +1101 -0
- package/.claude/skills/create-insight-skill/SKILL.md +1317 -0
- package/.claude/skills/get-insight-data-skill/SKILL.md +1053 -0
- package/.claude/skills/hailer-api/SKILL.md +283 -0
- package/.claude/skills/hailer-api/references/activities.md +620 -0
- package/.claude/skills/hailer-api/references/authentication.md +216 -0
- package/.claude/skills/hailer-api/references/datasets.md +437 -0
- package/.claude/skills/hailer-api/references/files.md +301 -0
- package/.claude/skills/hailer-api/references/insights.md +469 -0
- package/.claude/skills/hailer-api/references/workflows.md +720 -0
- package/.claude/skills/hailer-api/references/workspaces-users.md +445 -0
- package/.claude/skills/insight-api/SKILL.md +185 -0
- package/.claude/skills/insight-api/references/insight-endpoints.md +514 -0
- package/.claude/skills/install-workflow-skill/SKILL.md +1056 -0
- package/.claude/skills/list-apps-skill/SKILL.md +1010 -0
- package/.claude/skills/list-workflows-minimal-skill/SKILL.md +992 -0
- package/.claude/skills/local-first-skill/SKILL.md +570 -0
- package/.claude/skills/mcp-tools/SKILL.md +419 -0
- package/.claude/skills/mcp-tools/references/api-endpoints.md +499 -0
- package/.claude/skills/mcp-tools/references/data-structures.md +554 -0
- package/.claude/skills/mcp-tools/references/implementation-patterns.md +717 -0
- package/.claude/skills/preview-insight-skill/SKILL.md +1290 -0
- package/.claude/skills/publish-hailer-app-skill/SKILL.md +453 -0
- package/.claude/skills/remove-app-member-skill/SKILL.md +671 -0
- package/.claude/skills/remove-app-skill/SKILL.md +985 -0
- package/.claude/skills/remove-insight-skill/SKILL.md +1011 -0
- package/.claude/skills/remove-workflow-skill/SKILL.md +920 -0
- package/.claude/skills/scaffold-hailer-app-skill/SKILL.md +1034 -0
- package/.claude/skills/skill-testing/README.md +137 -0
- package/.claude/skills/skill-testing/SKILL.md +348 -0
- package/.claude/skills/skill-testing/references/test-patterns.md +705 -0
- package/.claude/skills/skill-testing/references/testing-guide.md +603 -0
- package/.claude/skills/skill-testing/references/validation-checklist.md +537 -0
- package/.claude/skills/tool-builder/SKILL.md +328 -0
- package/.claude/skills/update-app-skill/SKILL.md +970 -0
- package/.claude/skills/update-workflow-field-skill/SKILL.md +1098 -0
- package/.env.example +81 -0
- package/.mcp.json +13 -0
- package/README.md +297 -0
- package/dist/app.d.ts +4 -0
- package/dist/app.js +74 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +5 -0
- package/dist/client/adaptive-documentation-bot.d.ts +108 -0
- package/dist/client/adaptive-documentation-bot.js +475 -0
- package/dist/client/adaptive-documentation-types.d.ts +66 -0
- package/dist/client/adaptive-documentation-types.js +9 -0
- package/dist/client/agent-activity-bot.d.ts +51 -0
- package/dist/client/agent-activity-bot.js +166 -0
- package/dist/client/agent-tracker.d.ts +499 -0
- package/dist/client/agent-tracker.js +659 -0
- package/dist/client/description-updater.d.ts +56 -0
- package/dist/client/description-updater.js +259 -0
- package/dist/client/log-parser.d.ts +72 -0
- package/dist/client/log-parser.js +387 -0
- package/dist/client/mcp-client.d.ts +50 -0
- package/dist/client/mcp-client.js +532 -0
- package/dist/client/message-processor.d.ts +35 -0
- package/dist/client/message-processor.js +352 -0
- package/dist/client/multi-bot-manager.d.ts +24 -0
- package/dist/client/multi-bot-manager.js +74 -0
- package/dist/client/providers/anthropic-provider.d.ts +19 -0
- package/dist/client/providers/anthropic-provider.js +631 -0
- package/dist/client/providers/llm-provider.d.ts +47 -0
- package/dist/client/providers/llm-provider.js +367 -0
- package/dist/client/providers/openai-provider.d.ts +23 -0
- package/dist/client/providers/openai-provider.js +621 -0
- package/dist/client/simple-llm-caller.d.ts +19 -0
- package/dist/client/simple-llm-caller.js +100 -0
- package/dist/client/skill-generator.d.ts +81 -0
- package/dist/client/skill-generator.js +386 -0
- package/dist/client/test-adaptive-bot.d.ts +9 -0
- package/dist/client/test-adaptive-bot.js +82 -0
- package/dist/client/token-pricing.d.ts +38 -0
- package/dist/client/token-pricing.js +127 -0
- package/dist/client/token-tracker.d.ts +232 -0
- package/dist/client/token-tracker.js +457 -0
- package/dist/client/token-usage-bot.d.ts +53 -0
- package/dist/client/token-usage-bot.js +153 -0
- package/dist/client/tool-executor.d.ts +69 -0
- package/dist/client/tool-executor.js +159 -0
- package/dist/client/tool-schema-loader.d.ts +60 -0
- package/dist/client/tool-schema-loader.js +178 -0
- package/dist/client/types.d.ts +69 -0
- package/dist/client/types.js +7 -0
- package/dist/config.d.ts +162 -0
- package/dist/config.js +296 -0
- package/dist/core.d.ts +26 -0
- package/dist/core.js +147 -0
- package/dist/lib/context-manager.d.ts +111 -0
- package/dist/lib/context-manager.js +431 -0
- package/dist/lib/logger.d.ts +74 -0
- package/dist/lib/logger.js +277 -0
- package/dist/lib/materialize.d.ts +3 -0
- package/dist/lib/materialize.js +101 -0
- package/dist/lib/normalizedName.d.ts +7 -0
- package/dist/lib/normalizedName.js +48 -0
- package/dist/lib/prompt-length-manager.d.ts +81 -0
- package/dist/lib/prompt-length-manager.js +457 -0
- package/dist/lib/terminal-prompt.d.ts +9 -0
- package/dist/lib/terminal-prompt.js +108 -0
- package/dist/mcp/UserContextCache.d.ts +56 -0
- package/dist/mcp/UserContextCache.js +163 -0
- package/dist/mcp/auth.d.ts +2 -0
- package/dist/mcp/auth.js +29 -0
- package/dist/mcp/hailer-clients.d.ts +42 -0
- package/dist/mcp/hailer-clients.js +246 -0
- package/dist/mcp/signal-handler.d.ts +45 -0
- package/dist/mcp/signal-handler.js +317 -0
- package/dist/mcp/tool-registry.d.ts +100 -0
- package/dist/mcp/tool-registry.js +306 -0
- package/dist/mcp/tools/activity.d.ts +15 -0
- package/dist/mcp/tools/activity.js +955 -0
- package/dist/mcp/tools/app.d.ts +20 -0
- package/dist/mcp/tools/app.js +1488 -0
- package/dist/mcp/tools/discussion.d.ts +19 -0
- package/dist/mcp/tools/discussion.js +950 -0
- package/dist/mcp/tools/file.d.ts +15 -0
- package/dist/mcp/tools/file.js +119 -0
- package/dist/mcp/tools/insight.d.ts +17 -0
- package/dist/mcp/tools/insight.js +806 -0
- package/dist/mcp/tools/skill.d.ts +10 -0
- package/dist/mcp/tools/skill.js +279 -0
- package/dist/mcp/tools/user.d.ts +10 -0
- package/dist/mcp/tools/user.js +108 -0
- package/dist/mcp/tools/workflow-template.d.ts +19 -0
- package/dist/mcp/tools/workflow-template.js +822 -0
- package/dist/mcp/tools/workflow.d.ts +18 -0
- package/dist/mcp/tools/workflow.js +1362 -0
- package/dist/mcp/utils/api-errors.d.ts +45 -0
- package/dist/mcp/utils/api-errors.js +160 -0
- package/dist/mcp/utils/data-transformers.d.ts +102 -0
- package/dist/mcp/utils/data-transformers.js +194 -0
- package/dist/mcp/utils/file-upload.d.ts +33 -0
- package/dist/mcp/utils/file-upload.js +148 -0
- package/dist/mcp/utils/hailer-api-client.d.ts +120 -0
- package/dist/mcp/utils/hailer-api-client.js +323 -0
- package/dist/mcp/utils/index.d.ts +13 -0
- package/dist/mcp/utils/index.js +39 -0
- package/dist/mcp/utils/logger.d.ts +42 -0
- package/dist/mcp/utils/logger.js +103 -0
- package/dist/mcp/utils/types.d.ts +286 -0
- package/dist/mcp/utils/types.js +7 -0
- package/dist/mcp/workspace-cache.d.ts +42 -0
- package/dist/mcp/workspace-cache.js +97 -0
- package/dist/mcp-server.d.ts +42 -0
- package/dist/mcp-server.js +280 -0
- package/package.json +56 -0
- 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
|