@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
|
+
* Token Usage Tracker
|
|
4
|
+
*
|
|
5
|
+
* Dedicated tracking system for token usage and costs across all bots.
|
|
6
|
+
* Optimized for both human readability and LLM analysis.
|
|
7
|
+
*
|
|
8
|
+
* Use Cases:
|
|
9
|
+
* - Monitor per-bot token budgets
|
|
10
|
+
* - Identify expensive tools/operations
|
|
11
|
+
* - Feed data to optimizer agents
|
|
12
|
+
* - Generate cost reports
|
|
13
|
+
*/
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.TokenTracker = void 0;
|
|
49
|
+
exports.createTokenTracker = createTokenTracker;
|
|
50
|
+
const logger_1 = require("../lib/logger");
|
|
51
|
+
const fs = __importStar(require("fs"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
/**
|
|
54
|
+
* Token Usage Tracker
|
|
55
|
+
*
|
|
56
|
+
* Tracks token usage across all bots and tools for cost optimization.
|
|
57
|
+
* Data is stored in a structured format for easy analysis by humans and agents.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const tracker = new TokenTracker();
|
|
62
|
+
*
|
|
63
|
+
* // Log bot activity
|
|
64
|
+
* tracker.logUsage({
|
|
65
|
+
* botId: 'bot_sales_789',
|
|
66
|
+
* botName: 'Sales Bot',
|
|
67
|
+
* provider: 'anthropic',
|
|
68
|
+
* conversationId: 'disc_123',
|
|
69
|
+
* toolsCalled: ['list_activities', 'create_activity'],
|
|
70
|
+
* tokens: {
|
|
71
|
+
* input: 24567,
|
|
72
|
+
* output: 412,
|
|
73
|
+
* total: 24979,
|
|
74
|
+
* cacheRead: 22000,
|
|
75
|
+
* cost: 0.038
|
|
76
|
+
* },
|
|
77
|
+
* duration: 2450,
|
|
78
|
+
* success: true
|
|
79
|
+
* });
|
|
80
|
+
*
|
|
81
|
+
* // Query for analyzer agent
|
|
82
|
+
* const botUsage = tracker.getBotUsage('bot_sales_789');
|
|
83
|
+
* const expensiveTools = tracker.getMostExpensiveTools(5);
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
class TokenTracker {
|
|
87
|
+
logger;
|
|
88
|
+
dataFilePath;
|
|
89
|
+
data;
|
|
90
|
+
maxRecentEvents = 100;
|
|
91
|
+
maxDailySummaries = 90; // Keep 90 days
|
|
92
|
+
constructor(logger, logDir = 'logs') {
|
|
93
|
+
this.logger = logger || (0, logger_1.createLogger)({ component: 'token-tracker' });
|
|
94
|
+
this.dataFilePath = path.join(logDir, 'token-usage.json');
|
|
95
|
+
// Initialize or load existing data
|
|
96
|
+
this.data = this.loadData();
|
|
97
|
+
this.logger.info('Token tracker initialized', {
|
|
98
|
+
dataFile: this.dataFilePath,
|
|
99
|
+
totalBots: this.data.summary.totalBots,
|
|
100
|
+
totalCost: this.data.summary.totalCost,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Load existing data or initialize new structure
|
|
105
|
+
*/
|
|
106
|
+
loadData() {
|
|
107
|
+
try {
|
|
108
|
+
if (fs.existsSync(this.dataFilePath)) {
|
|
109
|
+
const fileContent = fs.readFileSync(this.dataFilePath, 'utf8');
|
|
110
|
+
const data = JSON.parse(fileContent);
|
|
111
|
+
this.logger.info('Loaded existing token usage data', {
|
|
112
|
+
totalCost: data.summary.totalCost,
|
|
113
|
+
totalConversations: data.summary.totalConversations,
|
|
114
|
+
});
|
|
115
|
+
return data;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
this.logger.error('Failed to load token usage data, initializing new', error);
|
|
120
|
+
}
|
|
121
|
+
// Initialize new data structure
|
|
122
|
+
return {
|
|
123
|
+
summary: {
|
|
124
|
+
totalCost: 0,
|
|
125
|
+
totalTokens: 0,
|
|
126
|
+
totalConversations: 0,
|
|
127
|
+
totalBots: 0,
|
|
128
|
+
totalTools: 0,
|
|
129
|
+
mostExpensiveBot: null,
|
|
130
|
+
mostExpensiveTool: null,
|
|
131
|
+
period: {
|
|
132
|
+
start: new Date().toISOString().split('T')[0],
|
|
133
|
+
end: new Date().toISOString().split('T')[0],
|
|
134
|
+
},
|
|
135
|
+
lastUpdated: new Date().toISOString(),
|
|
136
|
+
},
|
|
137
|
+
bots: {},
|
|
138
|
+
tools: {},
|
|
139
|
+
dailySummaries: [],
|
|
140
|
+
recentEvents: [],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Save data to disk
|
|
145
|
+
*/
|
|
146
|
+
saveData() {
|
|
147
|
+
try {
|
|
148
|
+
// Ensure directory exists
|
|
149
|
+
const dir = path.dirname(this.dataFilePath);
|
|
150
|
+
if (!fs.existsSync(dir)) {
|
|
151
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
152
|
+
}
|
|
153
|
+
// Update last updated timestamp
|
|
154
|
+
this.data.summary.lastUpdated = new Date().toISOString();
|
|
155
|
+
// Write with pretty formatting for human readability
|
|
156
|
+
fs.writeFileSync(this.dataFilePath, JSON.stringify(this.data, null, 2), 'utf8');
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
this.logger.error('Failed to save token usage data', error);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Log a token usage event from a bot interaction
|
|
164
|
+
*/
|
|
165
|
+
logUsage(event) {
|
|
166
|
+
const fullEvent = {
|
|
167
|
+
...event,
|
|
168
|
+
timestamp: new Date().toISOString(),
|
|
169
|
+
};
|
|
170
|
+
try {
|
|
171
|
+
// Add to recent events (with rotation)
|
|
172
|
+
this.data.recentEvents.push(fullEvent);
|
|
173
|
+
if (this.data.recentEvents.length > this.maxRecentEvents) {
|
|
174
|
+
this.data.recentEvents.shift();
|
|
175
|
+
}
|
|
176
|
+
// Update bot statistics
|
|
177
|
+
this.updateBotStats(fullEvent);
|
|
178
|
+
// Update tool statistics
|
|
179
|
+
this.updateToolStats(fullEvent);
|
|
180
|
+
// Update daily summary
|
|
181
|
+
this.updateDailySummary(fullEvent);
|
|
182
|
+
// Update global summary
|
|
183
|
+
this.updateGlobalSummary(fullEvent);
|
|
184
|
+
// Save to disk
|
|
185
|
+
this.saveData();
|
|
186
|
+
this.logger.info('Token usage logged', {
|
|
187
|
+
botId: event.botId,
|
|
188
|
+
cost: event.tokens.cost,
|
|
189
|
+
tokens: event.tokens.total,
|
|
190
|
+
tools: event.toolsCalled.length,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
this.logger.error('Failed to log token usage', error, {
|
|
195
|
+
botId: event.botId,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Update per-bot statistics
|
|
201
|
+
*/
|
|
202
|
+
updateBotStats(event) {
|
|
203
|
+
const { botId, botName, tokens, toolsCalled } = event;
|
|
204
|
+
if (!this.data.bots[botId]) {
|
|
205
|
+
// Initialize new bot
|
|
206
|
+
this.data.bots[botId] = {
|
|
207
|
+
botId,
|
|
208
|
+
botName,
|
|
209
|
+
totalCost: 0,
|
|
210
|
+
totalTokens: 0,
|
|
211
|
+
conversations: 0,
|
|
212
|
+
averageCostPerConversation: 0,
|
|
213
|
+
averageTokensPerConversation: 0,
|
|
214
|
+
tools: {},
|
|
215
|
+
firstSeen: event.timestamp,
|
|
216
|
+
lastSeen: event.timestamp,
|
|
217
|
+
};
|
|
218
|
+
this.data.summary.totalBots++;
|
|
219
|
+
}
|
|
220
|
+
const bot = this.data.bots[botId];
|
|
221
|
+
// Update totals
|
|
222
|
+
bot.totalCost += tokens.cost;
|
|
223
|
+
bot.totalTokens += tokens.total;
|
|
224
|
+
bot.conversations++;
|
|
225
|
+
bot.lastSeen = event.timestamp;
|
|
226
|
+
// Update averages
|
|
227
|
+
bot.averageCostPerConversation = bot.totalCost / bot.conversations;
|
|
228
|
+
bot.averageTokensPerConversation = bot.totalTokens / bot.conversations;
|
|
229
|
+
// Update cache efficiency (if applicable)
|
|
230
|
+
if (tokens.cacheRead !== undefined || tokens.cacheCreation !== undefined) {
|
|
231
|
+
if (!bot.cacheEfficiency) {
|
|
232
|
+
bot.cacheEfficiency = {
|
|
233
|
+
totalCacheRead: 0,
|
|
234
|
+
totalCacheCreation: 0,
|
|
235
|
+
savingsPercent: 0,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
bot.cacheEfficiency.totalCacheRead += tokens.cacheRead || 0;
|
|
239
|
+
bot.cacheEfficiency.totalCacheCreation += tokens.cacheCreation || 0;
|
|
240
|
+
const totalInput = bot.totalTokens;
|
|
241
|
+
const cacheReads = bot.cacheEfficiency.totalCacheRead;
|
|
242
|
+
bot.cacheEfficiency.savingsPercent = totalInput > 0
|
|
243
|
+
? Math.round((cacheReads / totalInput) * 100)
|
|
244
|
+
: 0;
|
|
245
|
+
}
|
|
246
|
+
// Update per-tool stats within this bot
|
|
247
|
+
for (const toolName of toolsCalled) {
|
|
248
|
+
if (!bot.tools[toolName]) {
|
|
249
|
+
bot.tools[toolName] = {
|
|
250
|
+
calls: 0,
|
|
251
|
+
tokens: 0,
|
|
252
|
+
cost: 0,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const toolStat = bot.tools[toolName];
|
|
256
|
+
toolStat.calls++;
|
|
257
|
+
// Distribute tokens/cost evenly across tools (approximation)
|
|
258
|
+
toolStat.tokens += Math.round(tokens.total / toolsCalled.length);
|
|
259
|
+
toolStat.cost += tokens.cost / toolsCalled.length;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Update per-tool statistics
|
|
264
|
+
*/
|
|
265
|
+
updateToolStats(event) {
|
|
266
|
+
const { botId, tokens, toolsCalled } = event;
|
|
267
|
+
for (const toolName of toolsCalled) {
|
|
268
|
+
if (!this.data.tools[toolName]) {
|
|
269
|
+
this.data.tools[toolName] = {
|
|
270
|
+
toolName,
|
|
271
|
+
totalCost: 0,
|
|
272
|
+
totalTokens: 0,
|
|
273
|
+
calls: 0,
|
|
274
|
+
averageCostPerCall: 0,
|
|
275
|
+
averageTokensPerCall: 0,
|
|
276
|
+
usedByBots: [],
|
|
277
|
+
};
|
|
278
|
+
this.data.summary.totalTools++;
|
|
279
|
+
}
|
|
280
|
+
const tool = this.data.tools[toolName];
|
|
281
|
+
// Update totals (distribute evenly across tools)
|
|
282
|
+
tool.calls++;
|
|
283
|
+
tool.totalTokens += Math.round(tokens.total / toolsCalled.length);
|
|
284
|
+
tool.totalCost += tokens.cost / toolsCalled.length;
|
|
285
|
+
// Update averages
|
|
286
|
+
tool.averageCostPerCall = tool.totalCost / tool.calls;
|
|
287
|
+
tool.averageTokensPerCall = tool.totalTokens / tool.calls;
|
|
288
|
+
// Track which bots use this tool
|
|
289
|
+
if (!tool.usedByBots.includes(botId)) {
|
|
290
|
+
tool.usedByBots.push(botId);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Update daily summary
|
|
296
|
+
*/
|
|
297
|
+
updateDailySummary(event) {
|
|
298
|
+
const date = event.timestamp.split('T')[0]; // YYYY-MM-DD
|
|
299
|
+
let daily = this.data.dailySummaries.find(d => d.date === date);
|
|
300
|
+
if (!daily) {
|
|
301
|
+
daily = {
|
|
302
|
+
date,
|
|
303
|
+
cost: 0,
|
|
304
|
+
tokens: 0,
|
|
305
|
+
conversations: 0,
|
|
306
|
+
uniqueBots: 0,
|
|
307
|
+
topBot: '',
|
|
308
|
+
topTool: '',
|
|
309
|
+
};
|
|
310
|
+
this.data.dailySummaries.push(daily);
|
|
311
|
+
// Sort by date and trim old entries
|
|
312
|
+
this.data.dailySummaries.sort((a, b) => a.date.localeCompare(b.date));
|
|
313
|
+
if (this.data.dailySummaries.length > this.maxDailySummaries) {
|
|
314
|
+
this.data.dailySummaries.shift();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
daily.cost += event.tokens.cost;
|
|
318
|
+
daily.tokens += event.tokens.total;
|
|
319
|
+
daily.conversations++;
|
|
320
|
+
// Update period range
|
|
321
|
+
if (date < this.data.summary.period.start) {
|
|
322
|
+
this.data.summary.period.start = date;
|
|
323
|
+
}
|
|
324
|
+
if (date > this.data.summary.period.end) {
|
|
325
|
+
this.data.summary.period.end = date;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Update global summary statistics
|
|
330
|
+
*/
|
|
331
|
+
updateGlobalSummary(event) {
|
|
332
|
+
this.data.summary.totalCost += event.tokens.cost;
|
|
333
|
+
this.data.summary.totalTokens += event.tokens.total;
|
|
334
|
+
this.data.summary.totalConversations++;
|
|
335
|
+
// Find most expensive bot
|
|
336
|
+
let maxBotCost = 0;
|
|
337
|
+
let maxBotId = null;
|
|
338
|
+
for (const [botId, bot] of Object.entries(this.data.bots)) {
|
|
339
|
+
if (bot.totalCost > maxBotCost) {
|
|
340
|
+
maxBotCost = bot.totalCost;
|
|
341
|
+
maxBotId = botId;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
this.data.summary.mostExpensiveBot = maxBotId;
|
|
345
|
+
// Find most expensive tool
|
|
346
|
+
let maxToolCost = 0;
|
|
347
|
+
let maxToolName = null;
|
|
348
|
+
for (const [toolName, tool] of Object.entries(this.data.tools)) {
|
|
349
|
+
if (tool.totalCost > maxToolCost) {
|
|
350
|
+
maxToolCost = tool.totalCost;
|
|
351
|
+
maxToolName = toolName;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
this.data.summary.mostExpensiveTool = maxToolName;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Get usage statistics for a specific bot
|
|
358
|
+
* Useful for analyzer agents to investigate specific bots
|
|
359
|
+
*/
|
|
360
|
+
getBotUsage(botId) {
|
|
361
|
+
return this.data.bots[botId] || null;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Get usage statistics for a specific tool
|
|
365
|
+
*/
|
|
366
|
+
getToolUsage(toolName) {
|
|
367
|
+
return this.data.tools[toolName] || null;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Get N most expensive bots
|
|
371
|
+
*/
|
|
372
|
+
getMostExpensiveBots(limit = 5) {
|
|
373
|
+
return Object.values(this.data.bots)
|
|
374
|
+
.sort((a, b) => b.totalCost - a.totalCost)
|
|
375
|
+
.slice(0, limit);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Get N most expensive tools
|
|
379
|
+
*/
|
|
380
|
+
getMostExpensiveTools(limit = 5) {
|
|
381
|
+
return Object.values(this.data.tools)
|
|
382
|
+
.sort((a, b) => b.totalCost - a.totalCost)
|
|
383
|
+
.slice(0, limit);
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Get daily summaries for a date range
|
|
387
|
+
*/
|
|
388
|
+
getDailySummaries(startDate, endDate) {
|
|
389
|
+
let summaries = this.data.dailySummaries;
|
|
390
|
+
if (startDate) {
|
|
391
|
+
summaries = summaries.filter(s => s.date >= startDate);
|
|
392
|
+
}
|
|
393
|
+
if (endDate) {
|
|
394
|
+
summaries = summaries.filter(s => s.date <= endDate);
|
|
395
|
+
}
|
|
396
|
+
return summaries;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Get full data dump for analyzer agents
|
|
400
|
+
* This gives the agent complete context
|
|
401
|
+
*/
|
|
402
|
+
getFullData() {
|
|
403
|
+
return this.data;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Get summary statistics
|
|
407
|
+
*/
|
|
408
|
+
getSummary() {
|
|
409
|
+
return this.data.summary;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Generate human-readable report
|
|
413
|
+
*/
|
|
414
|
+
generateReport() {
|
|
415
|
+
const { summary, bots, tools } = this.data;
|
|
416
|
+
const report = `
|
|
417
|
+
Token Usage Report
|
|
418
|
+
==================
|
|
419
|
+
Period: ${summary.period.start} to ${summary.period.end}
|
|
420
|
+
Last Updated: ${summary.lastUpdated}
|
|
421
|
+
|
|
422
|
+
OVERALL SUMMARY
|
|
423
|
+
---------------
|
|
424
|
+
Total Cost: $${summary.totalCost.toFixed(2)}
|
|
425
|
+
Total Tokens: ${summary.totalTokens.toLocaleString()}
|
|
426
|
+
Total Conversations: ${summary.totalConversations}
|
|
427
|
+
Active Bots: ${summary.totalBots}
|
|
428
|
+
Tools Used: ${summary.totalTools}
|
|
429
|
+
|
|
430
|
+
TOP CONSUMERS
|
|
431
|
+
-------------
|
|
432
|
+
Most Expensive Bot: ${summary.mostExpensiveBot || 'N/A'}
|
|
433
|
+
Most Expensive Tool: ${summary.mostExpensiveTool || 'N/A'}
|
|
434
|
+
|
|
435
|
+
TOP 5 BOTS BY COST
|
|
436
|
+
------------------
|
|
437
|
+
${this.getMostExpensiveBots(5).map((bot, i) => `${i + 1}. ${bot.botName || bot.botId}: $${bot.totalCost.toFixed(2)} (${bot.conversations} conversations)`).join('\n')}
|
|
438
|
+
|
|
439
|
+
TOP 5 TOOLS BY COST
|
|
440
|
+
-------------------
|
|
441
|
+
${this.getMostExpensiveTools(5).map((tool, i) => `${i + 1}. ${tool.toolName}: $${tool.totalCost.toFixed(2)} (${tool.calls} calls)`).join('\n')}
|
|
442
|
+
|
|
443
|
+
RECENT DAILY USAGE
|
|
444
|
+
------------------
|
|
445
|
+
${this.data.dailySummaries.slice(-7).map(day => `${day.date}: $${day.cost.toFixed(2)} (${day.conversations} conversations)`).join('\n')}
|
|
446
|
+
`;
|
|
447
|
+
return report;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
exports.TokenTracker = TokenTracker;
|
|
451
|
+
/**
|
|
452
|
+
* Factory function to create a new TokenTracker instance
|
|
453
|
+
*/
|
|
454
|
+
function createTokenTracker(logger, logDir) {
|
|
455
|
+
return new TokenTracker(logger, logDir);
|
|
456
|
+
}
|
|
457
|
+
//# sourceMappingURL=token-tracker.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Usage Bot
|
|
3
|
+
*
|
|
4
|
+
* Monitors token usage and posts summaries to discussions.
|
|
5
|
+
* Can respond to queries about token costs and usage details.
|
|
6
|
+
*/
|
|
7
|
+
import { TokenTracker } from './token-tracker';
|
|
8
|
+
import { AgentTracker } from './agent-tracker';
|
|
9
|
+
export interface TokenUsageBotConfig {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
postSummaryAfterConversation: boolean;
|
|
12
|
+
minCostThreshold?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare class TokenUsageBot {
|
|
15
|
+
private tokenTracker;
|
|
16
|
+
private agentTracker;
|
|
17
|
+
private config;
|
|
18
|
+
private lastPostedConversation;
|
|
19
|
+
constructor(tokenTracker: TokenTracker, agentTracker: AgentTracker, config: TokenUsageBotConfig);
|
|
20
|
+
/**
|
|
21
|
+
* Generate a summary message for the last conversation in a discussion
|
|
22
|
+
*/
|
|
23
|
+
generateSummary(discussionId: string, conversationCost: number, conversationTokens: number): Promise<string>;
|
|
24
|
+
/**
|
|
25
|
+
* Format a concise summary message
|
|
26
|
+
*/
|
|
27
|
+
private formatSummaryMessage;
|
|
28
|
+
/**
|
|
29
|
+
* Generate detailed token usage report
|
|
30
|
+
*/
|
|
31
|
+
generateDetailedReport(discussionId?: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Check if message is asking for token details
|
|
34
|
+
*/
|
|
35
|
+
isTokenQuery(messageContent: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Should post summary after this conversation?
|
|
38
|
+
*/
|
|
39
|
+
shouldPostSummary(discussionId: string, cost: number): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Mark that we posted a summary for this discussion
|
|
42
|
+
*/
|
|
43
|
+
markPosted(discussionId: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Get token tracker instance
|
|
46
|
+
*/
|
|
47
|
+
getTokenTracker(): TokenTracker;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Factory function to create TokenUsageBot
|
|
51
|
+
*/
|
|
52
|
+
export declare function createTokenUsageBot(tokenTracker: TokenTracker, agentTracker: AgentTracker, config: TokenUsageBotConfig): TokenUsageBot;
|
|
53
|
+
//# sourceMappingURL=token-usage-bot.d.ts.map
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Token Usage Bot
|
|
4
|
+
*
|
|
5
|
+
* Monitors token usage and posts summaries to discussions.
|
|
6
|
+
* Can respond to queries about token costs and usage details.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.TokenUsageBot = void 0;
|
|
10
|
+
exports.createTokenUsageBot = createTokenUsageBot;
|
|
11
|
+
const logger_1 = require("../lib/logger");
|
|
12
|
+
const logger = (0, logger_1.createLogger)({ component: 'token-usage-bot' });
|
|
13
|
+
class TokenUsageBot {
|
|
14
|
+
tokenTracker;
|
|
15
|
+
agentTracker;
|
|
16
|
+
config;
|
|
17
|
+
lastPostedConversation = new Map(); // discussionId -> timestamp
|
|
18
|
+
constructor(tokenTracker, agentTracker, config) {
|
|
19
|
+
this.tokenTracker = tokenTracker;
|
|
20
|
+
this.agentTracker = agentTracker;
|
|
21
|
+
this.config = {
|
|
22
|
+
minCostThreshold: 0.01,
|
|
23
|
+
...config
|
|
24
|
+
};
|
|
25
|
+
if (this.config.enabled) {
|
|
26
|
+
logger.info('Token Usage Bot initialized', {
|
|
27
|
+
postSummaries: this.config.postSummaryAfterConversation,
|
|
28
|
+
minThreshold: this.config.minCostThreshold
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generate a summary message for the last conversation in a discussion
|
|
34
|
+
*/
|
|
35
|
+
async generateSummary(discussionId, conversationCost, conversationTokens) {
|
|
36
|
+
const summary = this.tokenTracker.getSummary();
|
|
37
|
+
const recentEvents = this.tokenTracker.getFullData().recentEvents.filter(e => e.conversationId === discussionId);
|
|
38
|
+
if (recentEvents.length === 0) {
|
|
39
|
+
return `💰 **Token Usage Summary**\n\nNo recent token data available for this conversation.`;
|
|
40
|
+
}
|
|
41
|
+
// Get the most recent event for this discussion
|
|
42
|
+
const lastEvent = recentEvents[recentEvents.length - 1];
|
|
43
|
+
return this.formatSummaryMessage(lastEvent, conversationCost, conversationTokens);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Format a concise summary message
|
|
47
|
+
*/
|
|
48
|
+
formatSummaryMessage(event, cost, tokens) {
|
|
49
|
+
const cacheInfo = event.tokens.cacheRead > 0
|
|
50
|
+
? ` (${event.tokens.cacheRead.toLocaleString()} cached)`
|
|
51
|
+
: '';
|
|
52
|
+
return `💰 **Token Usage**
|
|
53
|
+
Cost: $${cost.toFixed(4)} | Tokens: ${tokens.toLocaleString()}${cacheInfo}
|
|
54
|
+
Tools: ${event.toolsCalled.length > 0 ? event.toolsCalled.join(', ') : 'none'}`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Generate detailed token usage report
|
|
58
|
+
*/
|
|
59
|
+
generateDetailedReport(discussionId) {
|
|
60
|
+
const summary = this.tokenTracker.getSummary();
|
|
61
|
+
const data = this.tokenTracker.getFullData();
|
|
62
|
+
let report = `📊 **Detailed Token Usage Report**\n\n`;
|
|
63
|
+
if (discussionId) {
|
|
64
|
+
// Filter for specific discussion
|
|
65
|
+
const discussionEvents = data.recentEvents.filter(e => e.conversationId === discussionId);
|
|
66
|
+
const discussionCost = discussionEvents.reduce((sum, e) => sum + e.tokens.cost, 0);
|
|
67
|
+
const discussionTokens = discussionEvents.reduce((sum, e) => sum + e.tokens.total, 0);
|
|
68
|
+
report += `**This Conversation:**\n`;
|
|
69
|
+
report += `- Total Cost: $${discussionCost.toFixed(4)}\n`;
|
|
70
|
+
report += `- Total Tokens: ${discussionTokens.toLocaleString()}\n`;
|
|
71
|
+
report += `- Messages: ${discussionEvents.length}\n`;
|
|
72
|
+
report += `- Avg per message: $${(discussionCost / discussionEvents.length).toFixed(4)}\n\n`;
|
|
73
|
+
// Show last 5 events
|
|
74
|
+
report += `**Recent Messages:**\n`;
|
|
75
|
+
discussionEvents.slice(-5).forEach((event, i) => {
|
|
76
|
+
report += `${i + 1}. $${event.tokens.cost.toFixed(4)} - ${event.tokens.total.toLocaleString()} tokens`;
|
|
77
|
+
if (event.toolsCalled.length > 0) {
|
|
78
|
+
report += ` - Tools: ${event.toolsCalled.slice(0, 3).join(', ')}`;
|
|
79
|
+
if (event.toolsCalled.length > 3)
|
|
80
|
+
report += `...`;
|
|
81
|
+
}
|
|
82
|
+
report += `\n`;
|
|
83
|
+
});
|
|
84
|
+
report += `\n`;
|
|
85
|
+
}
|
|
86
|
+
// Overall stats
|
|
87
|
+
report += `**Overall Stats:**\n`;
|
|
88
|
+
report += `- Total Cost: $${summary.totalCost.toFixed(2)}\n`;
|
|
89
|
+
report += `- Total Tokens: ${summary.totalTokens.toLocaleString()}\n`;
|
|
90
|
+
report += `- Total Conversations: ${summary.totalConversations}\n`;
|
|
91
|
+
report += `- Avg per conversation: $${(summary.totalCost / summary.totalConversations).toFixed(4)}\n\n`;
|
|
92
|
+
// Top tools
|
|
93
|
+
const topTools = this.tokenTracker.getMostExpensiveTools(5);
|
|
94
|
+
if (topTools.length > 0) {
|
|
95
|
+
report += `**Most Expensive Tools:**\n`;
|
|
96
|
+
topTools.forEach((tool, i) => {
|
|
97
|
+
report += `${i + 1}. ${tool.toolName}: $${tool.totalCost.toFixed(4)} (${tool.calls} calls)\n`;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return report;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Check if message is asking for token details
|
|
104
|
+
*/
|
|
105
|
+
isTokenQuery(messageContent) {
|
|
106
|
+
const lower = messageContent.toLowerCase();
|
|
107
|
+
return (lower.includes('token') && (lower.includes('usage') ||
|
|
108
|
+
lower.includes('cost') ||
|
|
109
|
+
lower.includes('price') ||
|
|
110
|
+
lower.includes('detail') ||
|
|
111
|
+
lower.includes('report') ||
|
|
112
|
+
lower.includes('how much') ||
|
|
113
|
+
lower.includes('expensive')));
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Should post summary after this conversation?
|
|
117
|
+
*/
|
|
118
|
+
shouldPostSummary(discussionId, cost) {
|
|
119
|
+
if (!this.config.enabled || !this.config.postSummaryAfterConversation) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
// Check cost threshold
|
|
123
|
+
if (cost < (this.config.minCostThreshold || 0)) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
// Debounce: Don't post if we posted recently (within 30 seconds)
|
|
127
|
+
const lastPosted = this.lastPostedConversation.get(discussionId);
|
|
128
|
+
if (lastPosted && Date.now() - lastPosted < 30000) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Mark that we posted a summary for this discussion
|
|
135
|
+
*/
|
|
136
|
+
markPosted(discussionId) {
|
|
137
|
+
this.lastPostedConversation.set(discussionId, Date.now());
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get token tracker instance
|
|
141
|
+
*/
|
|
142
|
+
getTokenTracker() {
|
|
143
|
+
return this.tokenTracker;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
exports.TokenUsageBot = TokenUsageBot;
|
|
147
|
+
/**
|
|
148
|
+
* Factory function to create TokenUsageBot
|
|
149
|
+
*/
|
|
150
|
+
function createTokenUsageBot(tokenTracker, agentTracker, config) {
|
|
151
|
+
return new TokenUsageBot(tokenTracker, agentTracker, config);
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=token-usage-bot.js.map
|