@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,457 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Prompt Length Manager
|
|
4
|
+
* Handles prompt length validation, truncation, and debugging
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.promptLengthManager = exports.PromptLengthManager = exports.DEFAULT_TRUNCATION_CONFIG = void 0;
|
|
8
|
+
const logger_1 = require("./logger");
|
|
9
|
+
exports.DEFAULT_TRUNCATION_CONFIG = {
|
|
10
|
+
// Claude 4 optimized limits (leaving room for response)
|
|
11
|
+
maxTotalTokens: 150_000, // 150k tokens for Claude 4
|
|
12
|
+
maxUserMessagesTokens: 20_000, // 20k tokens for user messages
|
|
13
|
+
// Character limits (fallback: 1 token ā 4 characters)
|
|
14
|
+
maxTotalCharacters: 600_000, // ~150k tokens
|
|
15
|
+
maxUserMessagesCharacters: 80_000, // ~20k tokens
|
|
16
|
+
// Provider context windows (in tokens)
|
|
17
|
+
anthropicContextWindow: 200_000, // Claude Sonnet 4
|
|
18
|
+
openaiContextWindow: 128_000, // GPT-4 Turbo
|
|
19
|
+
// New smart truncation strategy
|
|
20
|
+
preserveSystemPrompt: true, // Never truncate system prompts
|
|
21
|
+
preserveUserMessages: true, // Preserve up to 20k tokens
|
|
22
|
+
preserveAssistantResponses: true, // Preserve when possible
|
|
23
|
+
truncateToolResponsesFirst: true, // Tool responses are most expendable
|
|
24
|
+
};
|
|
25
|
+
class PromptLengthManager {
|
|
26
|
+
logger;
|
|
27
|
+
config;
|
|
28
|
+
constructor(config = {}) {
|
|
29
|
+
this.config = { ...exports.DEFAULT_TRUNCATION_CONFIG, ...config };
|
|
30
|
+
this.logger = (0, logger_1.createLogger)({ component: "PromptLengthManager" });
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Estimate token count from character count
|
|
34
|
+
* Rough approximation: 1 token ā 4 characters for English text
|
|
35
|
+
*/
|
|
36
|
+
estimateTokens(text) {
|
|
37
|
+
return Math.ceil(text.length / 4);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Count exact characters and estimate tokens for a text
|
|
41
|
+
*/
|
|
42
|
+
analyzeText(text) {
|
|
43
|
+
const characters = text.length;
|
|
44
|
+
const estimatedTokens = this.estimateTokens(text);
|
|
45
|
+
return { characters, estimatedTokens };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Truncate text intelligently, preserving structure when possible
|
|
49
|
+
*/
|
|
50
|
+
truncateText(text, maxLength, source) {
|
|
51
|
+
if (text.length <= maxLength) {
|
|
52
|
+
return { text, truncated: false };
|
|
53
|
+
}
|
|
54
|
+
// Try to truncate at natural boundaries (paragraphs, sentences)
|
|
55
|
+
const truncated = text.substring(0, maxLength);
|
|
56
|
+
// Find last complete sentence or paragraph
|
|
57
|
+
const lastParagraph = truncated.lastIndexOf('\n\n');
|
|
58
|
+
const lastSentence = truncated.lastIndexOf('. ');
|
|
59
|
+
let cutPoint = maxLength;
|
|
60
|
+
let truncationMethod = 'hard_cutoff';
|
|
61
|
+
if (lastParagraph > maxLength * 0.7) {
|
|
62
|
+
cutPoint = lastParagraph;
|
|
63
|
+
truncationMethod = 'paragraph_boundary';
|
|
64
|
+
}
|
|
65
|
+
else if (lastSentence > maxLength * 0.8) {
|
|
66
|
+
cutPoint = lastSentence + 1;
|
|
67
|
+
truncationMethod = 'sentence_boundary';
|
|
68
|
+
}
|
|
69
|
+
const finalText = text.substring(0, cutPoint) + '\n\n[... Content truncated ...]';
|
|
70
|
+
const removedContent = text.substring(cutPoint);
|
|
71
|
+
this.logger.warn(`Truncated ${source}`, {
|
|
72
|
+
originalLength: text.length,
|
|
73
|
+
truncatedLength: finalText.length,
|
|
74
|
+
truncatedAt: cutPoint,
|
|
75
|
+
truncationMethod,
|
|
76
|
+
removedCharacters: removedContent.length,
|
|
77
|
+
reductionPercent: Math.round((1 - finalText.length / text.length) * 100),
|
|
78
|
+
source,
|
|
79
|
+
removedContentPreview: removedContent.substring(0, 200) + (removedContent.length > 200 ? '...' : ''),
|
|
80
|
+
originalContentPreview: text.substring(0, 200) + (text.length > 200 ? '...' : '')
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
text: finalText,
|
|
84
|
+
truncated: true,
|
|
85
|
+
truncatedAt: cutPoint,
|
|
86
|
+
truncationMethod,
|
|
87
|
+
originalContent: text
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Analyze prompt components and build analysis
|
|
92
|
+
*/
|
|
93
|
+
analyzePrompt(components) {
|
|
94
|
+
let totalCharacters = 0;
|
|
95
|
+
let totalTokens = 0;
|
|
96
|
+
const warnings = [];
|
|
97
|
+
const processedComponents = [];
|
|
98
|
+
for (const component of components) {
|
|
99
|
+
const analysis = this.analyzeText(component.content);
|
|
100
|
+
totalCharacters += analysis.characters;
|
|
101
|
+
totalTokens += analysis.estimatedTokens;
|
|
102
|
+
processedComponents.push({
|
|
103
|
+
...component,
|
|
104
|
+
originalLength: analysis.characters,
|
|
105
|
+
});
|
|
106
|
+
// TRUNCATION WARNINGS TEMPORARILY DISABLED FOR TESTING
|
|
107
|
+
/* COMMENTED OUT - RE-ENABLE BY UNCOMMENTING THIS BLOCK
|
|
108
|
+
// Check component token limits under new system
|
|
109
|
+
const componentTokens = this.estimateTokens(component.content);
|
|
110
|
+
|
|
111
|
+
if (component.type === 'user' && componentTokens > this.config.maxUserMessagesTokens) {
|
|
112
|
+
warnings.push(`User message (${componentTokens} tokens) exceeds user message limit (${this.config.maxUserMessagesTokens} tokens)`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Note: No individual limits for system prompts or tool responses under new logic
|
|
116
|
+
*/ // END OF COMMENTED OUT WARNING LOGIC
|
|
117
|
+
}
|
|
118
|
+
// TOTAL TOKEN LIMIT WARNINGS TEMPORARILY DISABLED FOR TESTING
|
|
119
|
+
/* COMMENTED OUT - RE-ENABLE BY UNCOMMENTING THIS BLOCK
|
|
120
|
+
// Check total token limits (primary check under new system)
|
|
121
|
+
if (totalTokens > this.config.maxTotalTokens) {
|
|
122
|
+
warnings.push(`Total prompt (${totalTokens} tokens) exceeds Claude 4 limit (${this.config.maxTotalTokens} tokens)`);
|
|
123
|
+
}
|
|
124
|
+
*/ // END OF COMMENTED OUT WARNING LOGIC
|
|
125
|
+
return {
|
|
126
|
+
totalCharacters,
|
|
127
|
+
estimatedTokens: totalTokens,
|
|
128
|
+
components: processedComponents,
|
|
129
|
+
truncationApplied: false,
|
|
130
|
+
warnings,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* No longer truncate individual tool responses - only when total prompt exceeds limits
|
|
135
|
+
* This method now just passes through content unchanged and logs for monitoring
|
|
136
|
+
* TEMPORARILY DISABLED - Even monitoring/logging is disabled
|
|
137
|
+
*/
|
|
138
|
+
truncateToolResponse(content, toolName) {
|
|
139
|
+
// TRUNCATION AND MONITORING TEMPORARILY DISABLED FOR TESTING
|
|
140
|
+
return content; // Pass through unchanged with no logging
|
|
141
|
+
/* COMMENTED OUT - RE-ENABLE BY UNCOMMENTING THIS BLOCK
|
|
142
|
+
this.logger.debug(`Tool response processed (no individual limits)`, {
|
|
143
|
+
toolName,
|
|
144
|
+
length: content.length,
|
|
145
|
+
estimatedTokens: this.estimateTokens(content),
|
|
146
|
+
note: "Individual tool limits removed - truncation only happens at total prompt level"
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
return content; // Pass through unchanged
|
|
150
|
+
*/ // END OF COMMENTED OUT MONITORING LOGIC
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Apply smart token-based truncation optimized for Claude 4
|
|
154
|
+
* TEMPORARILY DISABLED - All truncation logic commented out for testing
|
|
155
|
+
*/
|
|
156
|
+
truncatePrompt(analysis) {
|
|
157
|
+
// TRUNCATION TEMPORARILY DISABLED FOR TESTING
|
|
158
|
+
this.logger.info("Truncation is temporarily disabled - returning prompt unchanged", {
|
|
159
|
+
originalTokens: analysis.estimatedTokens,
|
|
160
|
+
originalChars: analysis.totalCharacters,
|
|
161
|
+
tokenLimit: this.config.maxTotalTokens,
|
|
162
|
+
status: "DISABLED"
|
|
163
|
+
});
|
|
164
|
+
return analysis;
|
|
165
|
+
/* COMMENTED OUT - RE-ENABLE BY UNCOMMENTING THIS BLOCK
|
|
166
|
+
if (analysis.estimatedTokens <= this.config.maxTotalTokens) {
|
|
167
|
+
return analysis; // No truncation needed
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const targetTokenReduction = analysis.estimatedTokens - this.config.maxTotalTokens;
|
|
171
|
+
|
|
172
|
+
this.logger.warn("Applying Claude 4 optimized prompt truncation", {
|
|
173
|
+
originalTokens: analysis.estimatedTokens,
|
|
174
|
+
originalChars: analysis.totalCharacters,
|
|
175
|
+
tokenLimit: this.config.maxTotalTokens,
|
|
176
|
+
targetTokenReduction,
|
|
177
|
+
truncationStrategy: "claude_4_smart_preservation"
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Track unknown component types for debugging
|
|
181
|
+
const knownTypes = new Set(['system', 'user', 'assistant', 'tool_result', 'tool_call']);
|
|
182
|
+
const unknownComponents = analysis.components.filter(c => !knownTypes.has(c.type));
|
|
183
|
+
if (unknownComponents.length > 0) {
|
|
184
|
+
this.logger.info("Unknown prompt component types detected", {
|
|
185
|
+
unknownTypes: unknownComponents.map(c => ({ type: c.type, source: c.source, length: c.content.length })),
|
|
186
|
+
count: unknownComponents.length
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Separate components by type for smart preservation
|
|
191
|
+
const systemComponents = analysis.components.filter(c => c.type === 'system');
|
|
192
|
+
const userComponents = analysis.components.filter(c => c.type === 'user');
|
|
193
|
+
const assistantComponents = analysis.components.filter(c => c.type === 'assistant' || c.type === 'tool_call');
|
|
194
|
+
const toolComponents = analysis.components.filter(c => c.type === 'tool_result');
|
|
195
|
+
const otherComponents = analysis.components.filter(c => !knownTypes.has(c.type));
|
|
196
|
+
|
|
197
|
+
// Calculate current token usage by component type
|
|
198
|
+
const systemTokens = systemComponents.reduce((sum, c) => sum + this.estimateTokens(c.content), 0);
|
|
199
|
+
const userTokens = userComponents.reduce((sum, c) => sum + this.estimateTokens(c.content), 0);
|
|
200
|
+
const assistantTokens = assistantComponents.reduce((sum, c) => sum + this.estimateTokens(c.content), 0);
|
|
201
|
+
const toolTokens = toolComponents.reduce((sum, c) => sum + this.estimateTokens(c.content), 0);
|
|
202
|
+
const otherTokens = otherComponents.reduce((sum, c) => sum + this.estimateTokens(c.content), 0);
|
|
203
|
+
|
|
204
|
+
this.logger.info("Component token breakdown", {
|
|
205
|
+
system: `${systemTokens} tokens (${systemComponents.length} components) - PRESERVED`,
|
|
206
|
+
user: `${userTokens} tokens (${userComponents.length} components) - PRESERVE UP TO 20k`,
|
|
207
|
+
assistant: `${assistantTokens} tokens (${assistantComponents.length} components) - PRESERVE WHEN POSSIBLE`,
|
|
208
|
+
tool_result: `${toolTokens} tokens (${toolComponents.length} components) - TRUNCATE FIRST`,
|
|
209
|
+
other: `${otherTokens} tokens (${otherComponents.length} components) - UNKNOWN TYPE`
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const truncatedComponents: PromptComponent[] = [];
|
|
213
|
+
let tokensUsed = 0;
|
|
214
|
+
let tokensReduced = 0;
|
|
215
|
+
|
|
216
|
+
// Step 1: Always preserve system prompts (100%)
|
|
217
|
+
systemComponents.forEach(component => {
|
|
218
|
+
truncatedComponents.push({ ...component });
|
|
219
|
+
tokensUsed += this.estimateTokens(component.content);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Step 2: Preserve user messages up to 20k tokens
|
|
223
|
+
let userTokensUsed = 0;
|
|
224
|
+
userComponents.forEach(component => {
|
|
225
|
+
const componentTokens = this.estimateTokens(component.content);
|
|
226
|
+
if (userTokensUsed + componentTokens <= this.config.maxUserMessagesTokens) {
|
|
227
|
+
// Keep this user message fully
|
|
228
|
+
truncatedComponents.push({ ...component });
|
|
229
|
+
tokensUsed += componentTokens;
|
|
230
|
+
userTokensUsed += componentTokens;
|
|
231
|
+
} else if (userTokensUsed < this.config.maxUserMessagesTokens) {
|
|
232
|
+
// Partial truncation to fit within user message limit
|
|
233
|
+
const allowedTokens = this.config.maxUserMessagesTokens - userTokensUsed;
|
|
234
|
+
const allowedChars = Math.floor(allowedTokens * 4); // Approximate conversion
|
|
235
|
+
|
|
236
|
+
const result = this.truncateText(component.content, allowedChars, component.source || 'user_message');
|
|
237
|
+
truncatedComponents.push({
|
|
238
|
+
...component,
|
|
239
|
+
content: result.text,
|
|
240
|
+
truncated: result.truncated,
|
|
241
|
+
originalContent: result.originalContent,
|
|
242
|
+
truncatedAt: result.truncatedAt,
|
|
243
|
+
truncationReason: `User message truncated to fit within ${this.config.maxUserMessagesTokens} token limit for user messages`
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const actualTokens = this.estimateTokens(result.text);
|
|
247
|
+
tokensUsed += actualTokens;
|
|
248
|
+
userTokensUsed += actualTokens;
|
|
249
|
+
tokensReduced += componentTokens - actualTokens;
|
|
250
|
+
|
|
251
|
+
if (result.truncated) {
|
|
252
|
+
this.logger.warn("User message truncated to fit limit", {
|
|
253
|
+
componentSource: component.source,
|
|
254
|
+
originalTokens: componentTokens,
|
|
255
|
+
truncatedTokens: actualTokens,
|
|
256
|
+
userTokenLimit: this.config.maxUserMessagesTokens
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// Skip user messages that don't fit in the 20k limit
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Step 3: Add assistant responses if we have room
|
|
264
|
+
assistantComponents.forEach(component => {
|
|
265
|
+
const componentTokens = this.estimateTokens(component.content);
|
|
266
|
+
if (tokensUsed + componentTokens <= this.config.maxTotalTokens - targetTokenReduction) {
|
|
267
|
+
truncatedComponents.push({ ...component });
|
|
268
|
+
tokensUsed += componentTokens;
|
|
269
|
+
}
|
|
270
|
+
// Skip assistant messages if no room
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Step 4: Add tool responses with truncation if needed
|
|
274
|
+
const remainingTokenBudget = this.config.maxTotalTokens - tokensUsed;
|
|
275
|
+
let toolBudgetUsed = 0;
|
|
276
|
+
|
|
277
|
+
toolComponents.forEach(component => {
|
|
278
|
+
const componentTokens = this.estimateTokens(component.content);
|
|
279
|
+
|
|
280
|
+
if (toolBudgetUsed + componentTokens <= remainingTokenBudget) {
|
|
281
|
+
// Tool response fits fully
|
|
282
|
+
truncatedComponents.push({ ...component });
|
|
283
|
+
tokensUsed += componentTokens;
|
|
284
|
+
toolBudgetUsed += componentTokens;
|
|
285
|
+
} else if (toolBudgetUsed < remainingTokenBudget) {
|
|
286
|
+
// Partial truncation of tool response
|
|
287
|
+
const allowedTokens = remainingTokenBudget - toolBudgetUsed;
|
|
288
|
+
const allowedChars = Math.floor(allowedTokens * 4);
|
|
289
|
+
|
|
290
|
+
const result = this.truncateText(component.content, allowedChars, component.source || 'tool_result');
|
|
291
|
+
truncatedComponents.push({
|
|
292
|
+
...component,
|
|
293
|
+
content: result.text,
|
|
294
|
+
truncated: result.truncated,
|
|
295
|
+
originalContent: result.originalContent,
|
|
296
|
+
truncatedAt: result.truncatedAt,
|
|
297
|
+
truncationReason: `Tool response truncated to fit within ${this.config.maxTotalTokens} token total limit`
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
const actualTokens = this.estimateTokens(result.text);
|
|
301
|
+
tokensUsed += actualTokens;
|
|
302
|
+
toolBudgetUsed += actualTokens;
|
|
303
|
+
tokensReduced += componentTokens - actualTokens;
|
|
304
|
+
|
|
305
|
+
if (result.truncated) {
|
|
306
|
+
this.logger.warn("Tool response truncated", {
|
|
307
|
+
toolSource: component.source,
|
|
308
|
+
originalTokens: componentTokens,
|
|
309
|
+
truncatedTokens: actualTokens,
|
|
310
|
+
originalLength: component.content.length,
|
|
311
|
+
truncatedLength: result.text.length,
|
|
312
|
+
truncationMethod: result.truncationMethod,
|
|
313
|
+
truncatedAt: result.truncatedAt
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Skip tool responses that don't fit
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Step 5: Handle unknown component types
|
|
321
|
+
otherComponents.forEach(component => {
|
|
322
|
+
this.logger.warn("Unknown component type in prompt", {
|
|
323
|
+
type: component.type,
|
|
324
|
+
source: component.source,
|
|
325
|
+
length: component.content.length,
|
|
326
|
+
tokens: this.estimateTokens(component.content),
|
|
327
|
+
note: "Added to debug page for analysis"
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Add to debug but don't include in prompt for now
|
|
331
|
+
truncatedComponents.push({
|
|
332
|
+
...component,
|
|
333
|
+
truncationReason: `Unknown component type '${component.type}' - review on debug page`
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const finalTokens = this.estimateTokens(truncatedComponents.map(c => c.content).join(''));
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
totalCharacters: truncatedComponents.reduce((sum, c) => sum + c.content.length, 0),
|
|
341
|
+
estimatedTokens: finalTokens,
|
|
342
|
+
components: truncatedComponents,
|
|
343
|
+
truncationApplied: tokensReduced > 0,
|
|
344
|
+
warnings: [
|
|
345
|
+
...analysis.warnings,
|
|
346
|
+
`Applied Claude 4 smart truncation: reduced by ${tokensReduced} tokens (${Math.round(tokensReduced / analysis.estimatedTokens * 100)}%)`,
|
|
347
|
+
`Preservation strategy: System (100%) ā User (up to 20k tokens) ā Assistant ā Tools`,
|
|
348
|
+
`Final prompt: ${finalTokens.toLocaleString()} tokens (limit: ${this.config.maxTotalTokens.toLocaleString()})`
|
|
349
|
+
],
|
|
350
|
+
};
|
|
351
|
+
*/ // END OF COMMENTED OUT TRUNCATION LOGIC
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Check if a provider-specific error indicates prompt too long
|
|
355
|
+
*/
|
|
356
|
+
isPromptTooLongError(error, provider) {
|
|
357
|
+
const errorMessage = error.message.toLowerCase();
|
|
358
|
+
if (provider === 'anthropic') {
|
|
359
|
+
return errorMessage.includes('prompt too long') ||
|
|
360
|
+
errorMessage.includes('maximum context length') ||
|
|
361
|
+
errorMessage.includes('context_length_exceeded') ||
|
|
362
|
+
errorMessage.includes('token limit exceeded');
|
|
363
|
+
}
|
|
364
|
+
if (provider === 'openai') {
|
|
365
|
+
return errorMessage.includes('maximum context length') ||
|
|
366
|
+
errorMessage.includes('context_length_exceeded') ||
|
|
367
|
+
errorMessage.includes('token limit exceeded') ||
|
|
368
|
+
errorMessage.includes('reduce the length');
|
|
369
|
+
}
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Log comprehensive debug information about prompt structure
|
|
374
|
+
*/
|
|
375
|
+
logPromptDebugInfo(analysis, provider, botId) {
|
|
376
|
+
const debugData = {
|
|
377
|
+
provider,
|
|
378
|
+
botId,
|
|
379
|
+
summary: {
|
|
380
|
+
totalCharacters: analysis.totalCharacters,
|
|
381
|
+
estimatedTokens: analysis.estimatedTokens,
|
|
382
|
+
componentCount: analysis.components.length,
|
|
383
|
+
truncationApplied: analysis.truncationApplied,
|
|
384
|
+
warningCount: analysis.warnings.length,
|
|
385
|
+
},
|
|
386
|
+
breakdown: analysis.components.map(comp => ({
|
|
387
|
+
type: comp.type,
|
|
388
|
+
source: comp.source,
|
|
389
|
+
currentLength: comp.content.length,
|
|
390
|
+
originalLength: comp.originalLength || comp.content.length,
|
|
391
|
+
truncated: comp.truncated,
|
|
392
|
+
reductionPercent: comp.originalLength ?
|
|
393
|
+
Math.round((1 - comp.content.length / comp.originalLength) * 100) : 0,
|
|
394
|
+
estimatedTokens: this.estimateTokens(comp.content),
|
|
395
|
+
// First 100 chars as preview (for debugging)
|
|
396
|
+
preview: comp.content.substring(0, 100) + (comp.content.length > 100 ? '...' : ''),
|
|
397
|
+
})),
|
|
398
|
+
warnings: analysis.warnings,
|
|
399
|
+
};
|
|
400
|
+
this.logger.info("Prompt debug analysis", debugData);
|
|
401
|
+
// Enhanced truncation logging
|
|
402
|
+
const truncatedComponents = analysis.components.filter(c => c.truncated);
|
|
403
|
+
if (truncatedComponents.length > 0) {
|
|
404
|
+
this.logger.warn("Component truncation summary", {
|
|
405
|
+
truncatedCount: truncatedComponents.length,
|
|
406
|
+
totalComponents: analysis.components.length,
|
|
407
|
+
truncations: truncatedComponents.map(comp => ({
|
|
408
|
+
type: comp.type,
|
|
409
|
+
source: comp.source,
|
|
410
|
+
originalChars: comp.originalLength,
|
|
411
|
+
truncatedChars: comp.content.length,
|
|
412
|
+
savedChars: (comp.originalLength || comp.content.length) - comp.content.length,
|
|
413
|
+
reductionPercent: comp.originalLength ?
|
|
414
|
+
Math.round((1 - comp.content.length / comp.originalLength) * 100) : 0,
|
|
415
|
+
}))
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
// Log full prompt to separate file if it was truncated or has warnings
|
|
419
|
+
if (analysis.truncationApplied || analysis.warnings.length > 0) {
|
|
420
|
+
this.logger.warn("Prompt required attention", {
|
|
421
|
+
reason: analysis.truncationApplied ? 'truncation' : 'warnings',
|
|
422
|
+
fullPrompt: analysis.components.map(c => `=== ${c.type.toUpperCase()} (${c.source || 'unknown'}) ===\n${c.content}`).join('\n\n'),
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Generate a quick truncation summary for debugging
|
|
428
|
+
*/
|
|
429
|
+
generateTruncationSummary(analysis) {
|
|
430
|
+
const totalOriginal = analysis.components.reduce((sum, comp) => sum + (comp.originalLength || comp.content.length), 0);
|
|
431
|
+
const totalCurrent = analysis.totalCharacters;
|
|
432
|
+
const totalSaved = totalOriginal - totalCurrent;
|
|
433
|
+
const overallReduction = totalOriginal > 0 ? Math.round((totalSaved / totalOriginal) * 100) : 0;
|
|
434
|
+
const truncatedComponents = analysis.components.filter(c => c.truncated);
|
|
435
|
+
let summary = `Prompt Summary: ${totalCurrent.toLocaleString()} chars (${analysis.estimatedTokens.toLocaleString()} tokens)`;
|
|
436
|
+
if (totalSaved > 0) {
|
|
437
|
+
summary += `\nTruncation Applied: Saved ${totalSaved.toLocaleString()} chars (${overallReduction}% reduction)`;
|
|
438
|
+
summary += `\nTruncated Components: ${truncatedComponents.length}/${analysis.components.length}`;
|
|
439
|
+
if (truncatedComponents.length > 0) {
|
|
440
|
+
summary += `\nBreakdown:`;
|
|
441
|
+
truncatedComponents.forEach(comp => {
|
|
442
|
+
const saved = (comp.originalLength || comp.content.length) - comp.content.length;
|
|
443
|
+
const percent = comp.originalLength ? Math.round((saved / comp.originalLength) * 100) : 0;
|
|
444
|
+
summary += `\n - ${comp.type} (${comp.source}): ${saved.toLocaleString()} chars saved (${percent}%)`;
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
summary += `\nNo truncation applied`;
|
|
450
|
+
}
|
|
451
|
+
return summary;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
exports.PromptLengthManager = PromptLengthManager;
|
|
455
|
+
// Export a default instance
|
|
456
|
+
exports.promptLengthManager = new PromptLengthManager();
|
|
457
|
+
//# sourceMappingURL=prompt-length-manager.js.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.promptForCredentials = promptForCredentials;
|
|
37
|
+
const readline = __importStar(require("readline"));
|
|
38
|
+
const logger_1 = require("./logger");
|
|
39
|
+
const logger = (0, logger_1.createLogger)({ component: 'terminal-prompt' });
|
|
40
|
+
/**
|
|
41
|
+
* Prompts user for email and password in terminal
|
|
42
|
+
*/
|
|
43
|
+
function promptForCredentials() {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
const rl = readline.createInterface({
|
|
46
|
+
input: process.stdin,
|
|
47
|
+
output: process.stdout
|
|
48
|
+
});
|
|
49
|
+
logger.info('Prompting for Hailer credentials in Claude Code mode');
|
|
50
|
+
console.log(`\nš Please enter bot's or your own Hailer credentials. The Claude Code will use the account to access production data on Hailer.`);
|
|
51
|
+
rl.question('Email: ', (email) => {
|
|
52
|
+
if (!email.trim()) {
|
|
53
|
+
logger.error('Email validation failed: empty email provided');
|
|
54
|
+
console.log('ā Email cannot be empty');
|
|
55
|
+
rl.close();
|
|
56
|
+
reject(new Error('Email cannot be empty'));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
// Use a simple approach - just hide the password completely during input
|
|
60
|
+
rl.close();
|
|
61
|
+
process.stdout.write('Password: ');
|
|
62
|
+
// Disable echo temporarily
|
|
63
|
+
if (process.stdin.setRawMode) {
|
|
64
|
+
process.stdin.setRawMode(true);
|
|
65
|
+
}
|
|
66
|
+
process.stdin.resume();
|
|
67
|
+
process.stdin.setEncoding('utf8');
|
|
68
|
+
let password = '';
|
|
69
|
+
const cleanup = () => {
|
|
70
|
+
if (process.stdin.setRawMode) {
|
|
71
|
+
process.stdin.setRawMode(false);
|
|
72
|
+
}
|
|
73
|
+
process.stdin.pause();
|
|
74
|
+
};
|
|
75
|
+
process.stdin.on('data', function onData(char) {
|
|
76
|
+
const byte = char.charCodeAt(0);
|
|
77
|
+
if (byte === 3) { // Ctrl-C
|
|
78
|
+
cleanup();
|
|
79
|
+
process.stdin.removeListener('data', onData);
|
|
80
|
+
reject(new Error('Password input cancelled'));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (byte === 13 || byte === 10) { // Enter
|
|
84
|
+
cleanup();
|
|
85
|
+
process.stdin.removeListener('data', onData);
|
|
86
|
+
resolve({
|
|
87
|
+
email: email.trim(),
|
|
88
|
+
password: password.trim()
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (byte === 127 || byte === 8) { // Backspace
|
|
93
|
+
if (password.length > 0) {
|
|
94
|
+
password = password.slice(0, -1);
|
|
95
|
+
process.stdout.write('\b \b'); // Move back, write space, move back again
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// Add character to password and show asterisk
|
|
100
|
+
if (byte >= 32) {
|
|
101
|
+
password += char;
|
|
102
|
+
process.stdout.write('*');
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=terminal-prompt.js.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { HailerClient } from './hailer-clients';
|
|
2
|
+
import { WorkspaceCache } from './workspace-cache';
|
|
3
|
+
import { HailerV2CoreInitResponse, HailerApiClient } from './utils/index';
|
|
4
|
+
export interface UserContext {
|
|
5
|
+
client: HailerClient;
|
|
6
|
+
hailer: HailerApiClient;
|
|
7
|
+
init: HailerV2CoreInitResponse;
|
|
8
|
+
workspaceCache: WorkspaceCache;
|
|
9
|
+
apiKey: string;
|
|
10
|
+
createdAt: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Cache for user-specific data (client connections, init data, workspace cache)
|
|
14
|
+
*
|
|
15
|
+
* Replaces per-user ClassBasedToolManager instances with efficient caching:
|
|
16
|
+
* - Reuses shared connection pool from hailer-clients.ts
|
|
17
|
+
* - Caches user-specific data (init, workspaceCache) separately from tool logic
|
|
18
|
+
* - Used by both MCP Server and Client for consistent user context
|
|
19
|
+
*/
|
|
20
|
+
export declare class UserContextCache {
|
|
21
|
+
private static cache;
|
|
22
|
+
/** Life of each cache unit. */
|
|
23
|
+
private static readonly DEFAULT_TTL_MS;
|
|
24
|
+
/**
|
|
25
|
+
* Get or create user context (client, init, workspaceCache) for an API key
|
|
26
|
+
* This data is user-specific and should be cached per user
|
|
27
|
+
*
|
|
28
|
+
* @param apiKey - Hailer API key for authentication
|
|
29
|
+
* @param forceRefresh - If true, bypasses cache and creates fresh context (useful for testing or after permission changes)
|
|
30
|
+
* @param ttlMs - Custom TTL in milliseconds, defaults to 15 minutes
|
|
31
|
+
*/
|
|
32
|
+
static getContext(apiKey: string, forceRefresh?: boolean, ttlMs?: number): Promise<UserContext>;
|
|
33
|
+
/**
|
|
34
|
+
* Clear cache entry for specific API key (for cleanup/testing)
|
|
35
|
+
*/
|
|
36
|
+
static clearContext(apiKey: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Clear all cached contexts (for cleanup/testing)
|
|
39
|
+
*/
|
|
40
|
+
static clearAll(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Get cache statistics (for monitoring and debugging)
|
|
43
|
+
*
|
|
44
|
+
* @returns Cache stats including size, API keys, and age information
|
|
45
|
+
*/
|
|
46
|
+
static getCacheStats(): {
|
|
47
|
+
size: number;
|
|
48
|
+
apiKeys: string[];
|
|
49
|
+
entries: Array<{
|
|
50
|
+
apiKey: string;
|
|
51
|
+
ageMinutes: number;
|
|
52
|
+
isExpired: boolean;
|
|
53
|
+
}>;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=UserContextCache.d.ts.map
|