@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,367 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LlmProvider = void 0;
|
|
4
|
+
const logger_1 = require("../../lib/logger");
|
|
5
|
+
const agent_tracker_1 = require("../agent-tracker");
|
|
6
|
+
const prompt_length_manager_1 = require("../../lib/prompt-length-manager");
|
|
7
|
+
const UserContextCache_1 = require("../../mcp/UserContextCache");
|
|
8
|
+
// TODO:
|
|
9
|
+
// Move all message responding logic out of LLMProvider to a separate place.
|
|
10
|
+
// Read config from a global variable.
|
|
11
|
+
// Replace LLM provider pattern with thin API wrappers.
|
|
12
|
+
class LlmProvider {
|
|
13
|
+
name;
|
|
14
|
+
type;
|
|
15
|
+
config;
|
|
16
|
+
logger;
|
|
17
|
+
agentTracker;
|
|
18
|
+
promptLengthManager;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.config = config;
|
|
21
|
+
this.name = config.name;
|
|
22
|
+
this.type = config.type;
|
|
23
|
+
this.logger = (0, logger_1.createLogger)({
|
|
24
|
+
component: `${config.type}Provider`,
|
|
25
|
+
provider: config.name,
|
|
26
|
+
});
|
|
27
|
+
this.agentTracker = (0, agent_tracker_1.createAgentTracker)(this.logger);
|
|
28
|
+
this.promptLengthManager = new prompt_length_manager_1.PromptLengthManager();
|
|
29
|
+
}
|
|
30
|
+
isEnabled() {
|
|
31
|
+
return this.config.enabled && !!this.config.apiKey;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get the confirmation prompt template
|
|
35
|
+
*/
|
|
36
|
+
getConfirmationPrompt(userMessage) {
|
|
37
|
+
return `You are an AI assistant. Generate a brief, friendly confirmation message (1-2 sentences) that acknowledges what the user is asking for and indicates you're working on it. Make it clear that you'll follow up with the results shortly. Be conversational and personalized.
|
|
38
|
+
|
|
39
|
+
User message: "${this.removeMentions(userMessage)}"
|
|
40
|
+
User name: ${userMessage.userName}
|
|
41
|
+
|
|
42
|
+
Examples of good confirmations:
|
|
43
|
+
- "I'll help you find information about the project status and get back to you shortly with the results."
|
|
44
|
+
- "I'll look into that workflow for you right away and share what I find."
|
|
45
|
+
- "Let me check the recent activities and I'll have the details for you in a moment."
|
|
46
|
+
- "I'll analyze that data and provide you with the complete results shortly."
|
|
47
|
+
|
|
48
|
+
IMPORTANT: Respond in the same language as the user message. Respond very concisely.
|
|
49
|
+
|
|
50
|
+
Generate a similar confirmation message:`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Fetch additional context for the bot to better understand the current situation
|
|
54
|
+
*/
|
|
55
|
+
async fetchContextualInformation(mcpServerUrl, mcpServerApiKey, message) {
|
|
56
|
+
let workspaceInfo = "";
|
|
57
|
+
let recentMessages = "";
|
|
58
|
+
try {
|
|
59
|
+
// Fetch current workspace information
|
|
60
|
+
const workspaceResponse = await this.callMcpTool(mcpServerUrl, mcpServerApiKey, {
|
|
61
|
+
name: "get_current_workspace",
|
|
62
|
+
arguments: {},
|
|
63
|
+
});
|
|
64
|
+
if (workspaceResponse?.content?.[0]?.text) {
|
|
65
|
+
workspaceInfo = workspaceResponse.content[0].text;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
this.logger.warn("Failed to fetch workspace context", {
|
|
70
|
+
error: error instanceof Error ? error.message : String(error),
|
|
71
|
+
});
|
|
72
|
+
workspaceInfo = "Unable to fetch current workspace information";
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
// Fetch recent discussion messages (last 10)
|
|
76
|
+
const messagesResponse = await this.callMcpTool(mcpServerUrl, mcpServerApiKey, {
|
|
77
|
+
name: "fetch_discussion_messages",
|
|
78
|
+
arguments: {
|
|
79
|
+
discussionId: message.discussionId,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
if (messagesResponse?.content?.[0]?.text) {
|
|
83
|
+
recentMessages = messagesResponse.content[0].text;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
this.logger.warn("Failed to fetch discussion messages", {
|
|
88
|
+
error: error instanceof Error ? error.message : String(error),
|
|
89
|
+
});
|
|
90
|
+
recentMessages = "Unable to fetch recent discussion messages";
|
|
91
|
+
}
|
|
92
|
+
return { workspaceInfo, recentMessages };
|
|
93
|
+
}
|
|
94
|
+
logToolCall(toolName, args, duration) {
|
|
95
|
+
this.logger.toolCall(toolName, this.name, args, { duration });
|
|
96
|
+
}
|
|
97
|
+
logError(error, context) {
|
|
98
|
+
this.logger.error(`${this.name} Provider Error (${context})`, error, {
|
|
99
|
+
provider: this.name,
|
|
100
|
+
context,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
async createSystemPrompt(options) {
|
|
104
|
+
const { userMessage, mcpServerUrl, mcpServerApiKey, botEmail } = options;
|
|
105
|
+
const agentSpecificPrompt = await this.fetchAgentSpecificPrompt(botEmail, mcpServerUrl, mcpServerApiKey);
|
|
106
|
+
const workspacesPrompt = await this.buildWorkspacesPrompt(mcpServerApiKey);
|
|
107
|
+
const systemPrompt = this.buildSystemPrompt(userMessage, agentSpecificPrompt, workspacesPrompt);
|
|
108
|
+
this.logger.debug("Generated system prompt", {
|
|
109
|
+
promptLength: systemPrompt.length,
|
|
110
|
+
hasAgentPrompt: !!agentSpecificPrompt,
|
|
111
|
+
});
|
|
112
|
+
return systemPrompt;
|
|
113
|
+
}
|
|
114
|
+
async buildWorkspacesPrompt(mcpServerApiKey) {
|
|
115
|
+
const userContext = await UserContextCache_1.UserContextCache.getContext(mcpServerApiKey);
|
|
116
|
+
return `- Workspaces
|
|
117
|
+
${Object.values(userContext.workspaceCache.allWorkspaces)
|
|
118
|
+
.map(({ _id, name }) => `\n - Name: ${name}\n - Id: ${_id}`)
|
|
119
|
+
.join()}`;
|
|
120
|
+
}
|
|
121
|
+
async fetchAgentSpecificPrompt(botEmail, mcpServerUrl, mcpServerApiKey) {
|
|
122
|
+
try {
|
|
123
|
+
// I. Find "HR for AI Agents" workspace and get its ID
|
|
124
|
+
const workspacesResponse = await this.callMcpTool(mcpServerUrl, mcpServerApiKey, {
|
|
125
|
+
name: "list_workspaces",
|
|
126
|
+
arguments: {},
|
|
127
|
+
});
|
|
128
|
+
// Extract workspace info from the response
|
|
129
|
+
let hrWorkspaceId = null;
|
|
130
|
+
if (workspacesResponse?.content?.[0]?.text) {
|
|
131
|
+
const workspaceText = workspacesResponse.content[0].text;
|
|
132
|
+
// Look for "HR for AI Agents" workspace in the response
|
|
133
|
+
const hrWorkspaceMatch = workspaceText.match(/\*\*HR for AI Agents\*\*[^]*?ID:\s*(\w{24})/i);
|
|
134
|
+
if (hrWorkspaceMatch) {
|
|
135
|
+
hrWorkspaceId = hrWorkspaceMatch[1];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (!hrWorkspaceId) {
|
|
139
|
+
this.logger.warn("HR for AI Agents workspace not found, no agent-specific prompt fetched", {
|
|
140
|
+
botEmail,
|
|
141
|
+
});
|
|
142
|
+
return "";
|
|
143
|
+
}
|
|
144
|
+
// II. Find 'Agent Directory' workflow and get its ID
|
|
145
|
+
const workflowsResponse = await this.callMcpTool(mcpServerUrl, mcpServerApiKey, {
|
|
146
|
+
name: "list_workflows",
|
|
147
|
+
arguments: {
|
|
148
|
+
workspaceId: hrWorkspaceId,
|
|
149
|
+
limit: 1000,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
// Look for the Agent Directory workflow
|
|
153
|
+
let agentDirectoryWorkflowId = null;
|
|
154
|
+
if (workflowsResponse?.content?.[0]?.text) {
|
|
155
|
+
const workflowText = workflowsResponse.content[0].text;
|
|
156
|
+
// Look for "🤖 Agent Directory" workflow - find the specific workflow object
|
|
157
|
+
const agentDirMatch = workflowText.match(/\{\s*"id":\s*"(\w{24})"[^}]*"name":\s*"🤖 Agent Directory"[^}]*\}/i);
|
|
158
|
+
if (agentDirMatch) {
|
|
159
|
+
agentDirectoryWorkflowId = agentDirMatch[1];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (!agentDirectoryWorkflowId) {
|
|
163
|
+
this.logger.warn("Agent Directory workflow not found in HR workspace, no agent-specific prompt fetched", {
|
|
164
|
+
botEmail,
|
|
165
|
+
hrWorkspaceId,
|
|
166
|
+
});
|
|
167
|
+
return "";
|
|
168
|
+
}
|
|
169
|
+
// III. Get workflow schema to find the email and description (agent specific prompt) fields' IDs
|
|
170
|
+
const workflowInfoResponse = await this.callMcpTool(mcpServerUrl, mcpServerApiKey, {
|
|
171
|
+
name: "get_workflow_info",
|
|
172
|
+
arguments: {
|
|
173
|
+
workflowId: agentDirectoryWorkflowId,
|
|
174
|
+
includeFields: true,
|
|
175
|
+
includePhases: true,
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
// Extract field IDs for email and description fields, and phase ID for deployed agents
|
|
179
|
+
let emailFieldId = null;
|
|
180
|
+
let descriptionFieldId = null;
|
|
181
|
+
let deployedAgentsPhaseId = null;
|
|
182
|
+
if (workflowInfoResponse?.content?.[0]?.text) {
|
|
183
|
+
const schemaText = workflowInfoResponse.content[0].text;
|
|
184
|
+
// Look for email field by finding the field with key "agentEmailInHailer"
|
|
185
|
+
const emailFieldMatch = schemaText.match(/"id":\s*"(\w{24})"[^}]*"key":\s*"agentEmailInHailer"/i);
|
|
186
|
+
if (emailFieldMatch) {
|
|
187
|
+
emailFieldId = emailFieldMatch[1];
|
|
188
|
+
}
|
|
189
|
+
// Look for description field by finding the field with key "agentmemoryThatAgentUsesToFindWhatToDo"
|
|
190
|
+
const descFieldMatch = schemaText.match(/"id":\s*"(\w{24})"[^}]*"key":\s*"agentmemoryThatAgentUsesToFindWhatToDo"/i);
|
|
191
|
+
if (descFieldMatch) {
|
|
192
|
+
descriptionFieldId = descFieldMatch[1];
|
|
193
|
+
}
|
|
194
|
+
// Look for "Deployed agents" phase ID by finding the ID that comes right before the name
|
|
195
|
+
const deployedPhaseMatch = schemaText.match(/"id":\s*"6836bcbb7f06312dd30dfd60"[^}]*"name":\s*"Deployed agents"/i);
|
|
196
|
+
if (deployedPhaseMatch) {
|
|
197
|
+
deployedAgentsPhaseId = "6836bcbb7f06312dd30dfd60";
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
// Fallback: try to extract it dynamically
|
|
201
|
+
const dynamicMatch = schemaText.match(/"name":\s*"Deployed agents"[^}]*"id":\s*"(\w{24})"|"id":\s*"(\w{24})"[^}]*"name":\s*"Deployed agents"/i);
|
|
202
|
+
if (dynamicMatch) {
|
|
203
|
+
deployedAgentsPhaseId = dynamicMatch[1] || dynamicMatch[2];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (!emailFieldId) {
|
|
208
|
+
this.logger.warn("Email field not found in Agent Directory schema", {
|
|
209
|
+
botEmail,
|
|
210
|
+
agentDirectoryWorkflowId,
|
|
211
|
+
});
|
|
212
|
+
return "";
|
|
213
|
+
}
|
|
214
|
+
// IV. Get the agent specific activity by email
|
|
215
|
+
// Filter activities to find the agent with matching email
|
|
216
|
+
const agentActivitiesResponse = await this.callMcpTool(mcpServerUrl, mcpServerApiKey, {
|
|
217
|
+
name: "filter_activities",
|
|
218
|
+
arguments: {
|
|
219
|
+
workflowId: agentDirectoryWorkflowId,
|
|
220
|
+
phaseId: deployedAgentsPhaseId,
|
|
221
|
+
filters: {
|
|
222
|
+
[emailFieldId]: {
|
|
223
|
+
operator: "equals",
|
|
224
|
+
value: botEmail,
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
limit: 1,
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
// Extract the agent description from the response
|
|
231
|
+
if (agentActivitiesResponse?.content?.[0]?.text) {
|
|
232
|
+
const responseText = agentActivitiesResponse.content[0].text;
|
|
233
|
+
// Try to find the description field directly from the text without JSON parsing
|
|
234
|
+
try {
|
|
235
|
+
// Look for the Description field value in the text response
|
|
236
|
+
const descriptionMatch = responseText.match(/"fieldName":\s*"Description",\s*"value":\s*"([^"]*(?:\\.[^"]*)*)"/);
|
|
237
|
+
if (descriptionMatch && descriptionMatch[1]) {
|
|
238
|
+
// Unescape JSON string
|
|
239
|
+
const description = descriptionMatch[1]
|
|
240
|
+
.replace(/\\"/g, '"')
|
|
241
|
+
.replace(/\\\\/g, "\\");
|
|
242
|
+
this.logger.info("Found agent-specific prompt", {
|
|
243
|
+
botEmail,
|
|
244
|
+
description: description.substring(0, 100) +
|
|
245
|
+
(description.length > 100 ? "..." : ""),
|
|
246
|
+
});
|
|
247
|
+
return description;
|
|
248
|
+
}
|
|
249
|
+
// Fallback: Try JSON parsing with better regex
|
|
250
|
+
const jsonMatch = responseText.match(/\[[\s\S]*\]/);
|
|
251
|
+
if (jsonMatch) {
|
|
252
|
+
const activities = JSON.parse(jsonMatch[0]);
|
|
253
|
+
if (activities.length > 0) {
|
|
254
|
+
const agentActivity = activities[0];
|
|
255
|
+
// Look for description in fieldsAndValues array (new format)
|
|
256
|
+
if (agentActivity.fieldsAndValues &&
|
|
257
|
+
Array.isArray(agentActivity.fieldsAndValues)) {
|
|
258
|
+
const descriptionField = agentActivity.fieldsAndValues.find((field) => field.fieldName === "Description");
|
|
259
|
+
if (descriptionField &&
|
|
260
|
+
descriptionField.value &&
|
|
261
|
+
typeof descriptionField.value === "string") {
|
|
262
|
+
this.logger.info("Found agent-specific prompt from fieldsAndValues", { botEmail });
|
|
263
|
+
return descriptionField.value;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// Fallback: Look in old format (fields object)
|
|
267
|
+
if (descriptionFieldId) {
|
|
268
|
+
const description = agentActivity.fields?.[descriptionFieldId]?.value ||
|
|
269
|
+
agentActivity.data?.[descriptionFieldId] ||
|
|
270
|
+
agentActivity[descriptionFieldId];
|
|
271
|
+
if (description && typeof description === "string") {
|
|
272
|
+
this.logger.info("Found agent-specific prompt from legacy fields", { botEmail });
|
|
273
|
+
return description;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch (parseError) {
|
|
280
|
+
this.logger.warn("Failed to parse agent activities response", {
|
|
281
|
+
parseError: parseError instanceof Error
|
|
282
|
+
? parseError.message
|
|
283
|
+
: String(parseError),
|
|
284
|
+
botEmail,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
this.logger.warn("No agent description found for email", { botEmail });
|
|
289
|
+
return "";
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
this.logger.warn("Agent-specific prompt not available (optional feature)", {
|
|
293
|
+
botEmail,
|
|
294
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
295
|
+
});
|
|
296
|
+
return "";
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
buildSystemPrompt(userMessage, agentSpecificPrompt, workspacesPrompt) {
|
|
300
|
+
const now = new Date();
|
|
301
|
+
const currentYear = now.getFullYear();
|
|
302
|
+
const currentMonth = now.toLocaleString('en-US', { month: 'long' });
|
|
303
|
+
const currentDate = now.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
|
|
304
|
+
const systemPrompt = `You are an AI assistant helping with Hailer workspace management.
|
|
305
|
+
|
|
306
|
+
⏰ CURRENT DATE & TIME (CRITICAL - USE THIS FOR ALL DATE CALCULATIONS):
|
|
307
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
308
|
+
📅 Today: ${currentDate}
|
|
309
|
+
📅 Current Year: ${currentYear}
|
|
310
|
+
📅 Current Month: ${currentMonth} ${currentYear}
|
|
311
|
+
|
|
312
|
+
IMPORTANT: When users mention months like "January", "February", "March" without a year:
|
|
313
|
+
- Use ${currentYear} (THIS year)
|
|
314
|
+
- "January" = January ${currentYear}, NOT any past year
|
|
315
|
+
- Calculate Unix timestamps for ${currentYear} dates
|
|
316
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
317
|
+
|
|
318
|
+
Context:
|
|
319
|
+
- User: ${userMessage.userName} (ID: ${userMessage.userId})
|
|
320
|
+
- Discussion: ${userMessage.discussionId}
|
|
321
|
+
${workspacesPrompt}
|
|
322
|
+
- Message: "${this.removeMentions(userMessage)}"
|
|
323
|
+
|
|
324
|
+
Core Knowledge of Hailer:
|
|
325
|
+
Hailer is a SaaS-based platform for managing business processes and communication.
|
|
326
|
+
1. The system is built around Workspaces (previously called Networks), which contain two core structures:
|
|
327
|
+
- Workflows (formerly Processes)
|
|
328
|
+
- Datasets
|
|
329
|
+
Both Workflows and Datasets can be viewed as tables of Activities, where each Activity is similar to a row containing structured fields.
|
|
330
|
+
Datasets hold static records, typically used for reference or background data.
|
|
331
|
+
Workflows are similar to datasets, but each activity includes a phase field, enabling Kanban visualization. Activities can also be viewed in a table view, one phase at a time.
|
|
332
|
+
To list activities, list workflows first.
|
|
333
|
+
2. Activities:
|
|
334
|
+
Activities are the core data units in Hailer. They can include many types of fields (text, date, user, number, linked activity, etc.) and support custom logic fields (function fields) that use simple code (e.g. JavaScript). These fields allow dynamic data updates, aggregations, and calculations based on related activities.
|
|
335
|
+
This makes Hailer a powerful, modular, and customizable platform—especially effective for automating workflows and managing structured data across teams.
|
|
336
|
+
Each activity also includes its own discussion thread, which users can join or leave. These discussions allow team members to collaborate directly in context, share files, and keep communication structured around specific tasks or data entries.
|
|
337
|
+
3. Each workspace contains feed (same idea as in Facebook).
|
|
338
|
+
4. Visibility and access are controlled via user roles and teams.
|
|
339
|
+
5. Workspace data is isolated, and cannot be shared across workspaces.
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
IMPORTANT INSTRUCTIONS:
|
|
343
|
+
|
|
344
|
+
- IF THERE IS A WORKFLOW RELATED TO THE USER'S QUESTION, USE list_activities TOOL. OTHERWISE USE search_activities TOOL.
|
|
345
|
+
- **CONVERSATION HISTORY**: Recent conversation history (last 10 messages) is already loaded and visible in the message context above. You can see previous messages directly - DO NOT call fetch_discussion_messages unless you specifically need older messages beyond what's already visible.
|
|
346
|
+
- Don't hesitate to call multiple tools to get a complete picture before providing your response.
|
|
347
|
+
- **DATE FILTERING**: When filtering activities by date ranges (months, weeks, etc.), ALWAYS use ${currentYear} unless the user explicitly mentions a different year. For example, "January" means "January ${currentYear}", not any past year. Calculate Unix timestamps based on the current year shown above.
|
|
348
|
+
- **COMPREHENSIVE ANALYSIS**: For generic/broad questions, combine quantitative data (counts, metrics, trends) with qualitative data (discussions, comments) from multiple sources before concluding. Don't rely on limited data for generic questions.
|
|
349
|
+
- **EXPLAIN YOUR REASONING**: For complex queries where you analyzed data, searched activities, or used multiple tools, end your response with a brief "How I analyzed this:" section (1-5 sentences) explaining what data you examined. Use user-friendly language (e.g., "I reviewed activities and discussions" not "I called list_activities tool"). For simple conversational questions (greetings, simple facts, clarifications), skip this section entirely.
|
|
350
|
+
|
|
351
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
352
|
+
SISKON SIIVOUS QUALITY CONTROL AI AGENT - DATE FIELD INSTRUCTIONS:
|
|
353
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
354
|
+
You are the Siskon siivous quality control AI agent. When asked about dates referring to quality control visits or customer service process tickets, ALWAYS base the analysis on the "date" field by default, which is the date the visit happened or the topic is about. Do NOT use the created or updated date fields unless the user specifically asks about those fields. When explaining your analysis, and applicable depending on the question, specify which date field you used (e.g., "I analyzed visits based on the 'date' field" or "I used the 'created' date field as requested").
|
|
355
|
+
|
|
356
|
+
${agentSpecificPrompt}`;
|
|
357
|
+
return systemPrompt;
|
|
358
|
+
}
|
|
359
|
+
removeMentions(message) {
|
|
360
|
+
return message.content
|
|
361
|
+
.replace(/\[hailerTag\|[^\]]+\]\([^)]+\)/g, "") // Remove hailerTag mentions
|
|
362
|
+
.replace(/@mcp-agent\s*/gi, "") // Fallback for old format
|
|
363
|
+
.trim();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
exports.LlmProvider = LlmProvider;
|
|
367
|
+
//# sourceMappingURL=llm-provider.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ChatMessage, McpResponse } from "../types";
|
|
2
|
+
import { LlmProvider } from "./llm-provider";
|
|
3
|
+
export declare class OpenAIProvider extends LlmProvider {
|
|
4
|
+
private client;
|
|
5
|
+
private contextManager;
|
|
6
|
+
private toolSchemaLoader;
|
|
7
|
+
private toolExecutor;
|
|
8
|
+
constructor(config: any);
|
|
9
|
+
generateConfirmationMessage(userMessage: ChatMessage): Promise<string>;
|
|
10
|
+
processMessage(userMessage: ChatMessage, mcpServerUrl: string, botMcpApiKey: string, botEmail: string): Promise<McpResponse>;
|
|
11
|
+
/**
|
|
12
|
+
* Convert tool index to OpenAI tool format
|
|
13
|
+
* Loads full schemas for all tools (for now - could optimize with on-demand loading later)
|
|
14
|
+
*/
|
|
15
|
+
private convertToolIndexToOpenAIFormat;
|
|
16
|
+
/**
|
|
17
|
+
* Build optimized tools list: full schemas for loaded tools, minimal stubs for others
|
|
18
|
+
*/
|
|
19
|
+
private buildOptimizedTools;
|
|
20
|
+
protected callMcpTool(mcpServerUrl: string, mcpServerApiKey: string, request: any): Promise<any>;
|
|
21
|
+
private handleDirectResponse;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=openai-provider.d.ts.map
|