@poolzin/pool-bot 2026.1.32 → 2026.1.34
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/dist/build-info.json
CHANGED
|
@@ -24,8 +24,6 @@ defaultOnSessionStart, defaultOnSessionEnd, registerDefaultHooks,
|
|
|
24
24
|
clearAllHooks, } from "./lifecycle-hooks.js";
|
|
25
25
|
// Integration with agent events
|
|
26
26
|
export { startLifecycleHooksIntegration, getIntegrationStats, isSessionTracked, getTrackedSessionInfo, clearTrackedSessions, removeTrackedSession, } from "./lifecycle-hooks-integration.js";
|
|
27
|
-
// Tool usage capture (Day 2 - ACTIVE, feature-flagged)
|
|
28
|
-
export { toolUsageCaptureHook, loadSessionToolUsage, saveSessionToolUsage, } from "./tool-usage-capture.js";
|
|
29
27
|
// Progressive disclosure (Day 3 - ACTIVE, feature-flagged, OPT-IN)
|
|
30
28
|
// Feature flag: PROGRESSIVE_DISCLOSURE_FLAGS.ENABLED = false (OFF by default)
|
|
31
29
|
// Safe: Returns error "feature disabled" if called without enabling flag
|
package/package.json
CHANGED
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool Usage Capture Hook
|
|
3
|
-
*
|
|
4
|
-
* Captures tool usage automatically via lifecycle hooks.
|
|
5
|
-
* Safe: never throws, async operations, feature-flagged.
|
|
6
|
-
*
|
|
7
|
-
* Integration:
|
|
8
|
-
* - Registers as onToolUse hook
|
|
9
|
-
* - Captures all tool calls automatically
|
|
10
|
-
* - Stores compressed data in SessionEntry
|
|
11
|
-
*/
|
|
12
|
-
import { TOOL_USAGE_ENABLED, TOOL_USAGE_MAX_RECORDS, createToolUsageRecord, updateToolStats, addToHistory, } from "./tool-usage-storage.js";
|
|
13
|
-
// ============================================================================
|
|
14
|
-
// Configuration
|
|
15
|
-
// ============================================================================
|
|
16
|
-
const TOOL_USAGE_BUFFER_FLUSH_INTERVAL = 30000; // 30 seconds
|
|
17
|
-
const TOOL_USAGE_BUFFER_MAX_SIZE = 50; // Flush after 50 tool calls
|
|
18
|
-
const toolUsageBuffer = new Map();
|
|
19
|
-
let bufferFlushScheduled = false;
|
|
20
|
-
// ============================================================================
|
|
21
|
-
// SessionEntry Extension (Type-safe, backward compatible)
|
|
22
|
-
// ============================================================================
|
|
23
|
-
/**
|
|
24
|
-
* Load tool usage from SessionEntry
|
|
25
|
-
* Safe: handles missing data, validates structure
|
|
26
|
-
*/
|
|
27
|
-
export async function loadSessionToolUsage(sessionKey) {
|
|
28
|
-
try {
|
|
29
|
-
// TODO: Integrate with actual SessionEntry in Phase 4
|
|
30
|
-
// For now, return in-memory placeholder
|
|
31
|
-
const existing = toolUsageBuffer.get(sessionKey);
|
|
32
|
-
if (existing && existing.length > 0) {
|
|
33
|
-
// Return computed stats from buffer
|
|
34
|
-
const tools = {};
|
|
35
|
-
for (const record of existing) {
|
|
36
|
-
if (!tools[record.toolName]) {
|
|
37
|
-
tools[record.toolName] = {
|
|
38
|
-
count: 0,
|
|
39
|
-
totalMs: 0,
|
|
40
|
-
avgMs: 0,
|
|
41
|
-
lastUsed: record.timestamp,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
tools[record.toolName] = updateToolStats(tools[record.toolName], record.executionTimeMs);
|
|
45
|
-
}
|
|
46
|
-
return {
|
|
47
|
-
version: 2,
|
|
48
|
-
sessionKey,
|
|
49
|
-
sessionId: sessionKey, // Placeholder
|
|
50
|
-
tools,
|
|
51
|
-
history: existing,
|
|
52
|
-
lastCaptured: Date.now(),
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
// Return empty usage
|
|
56
|
-
return {
|
|
57
|
-
version: 2,
|
|
58
|
-
sessionKey,
|
|
59
|
-
sessionId: sessionKey,
|
|
60
|
-
tools: {},
|
|
61
|
-
history: [],
|
|
62
|
-
lastCaptured: Date.now(),
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
catch (err) {
|
|
66
|
-
console.error("[tool-usage-capture] Failed to load tool usage:", err);
|
|
67
|
-
// Return empty usage on error
|
|
68
|
-
return {
|
|
69
|
-
version: 2,
|
|
70
|
-
sessionKey,
|
|
71
|
-
sessionId: sessionKey,
|
|
72
|
-
tools: {},
|
|
73
|
-
history: [],
|
|
74
|
-
lastCaptured: Date.now(),
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Save tool usage to SessionEntry
|
|
80
|
-
* Safe: validates data, handles errors gracefully
|
|
81
|
-
*/
|
|
82
|
-
export async function saveSessionToolUsage(sessionKey, usage) {
|
|
83
|
-
try {
|
|
84
|
-
// TODO: Integrate with actual SessionEntry in Phase 4
|
|
85
|
-
// For now, just update in-memory buffer
|
|
86
|
-
const existing = toolUsageBuffer.get(sessionKey) || [];
|
|
87
|
-
const newRecords = usage.history.slice(existing.length);
|
|
88
|
-
if (newRecords.length > 0) {
|
|
89
|
-
toolUsageBuffer.set(sessionKey, [...existing, ...newRecords]);
|
|
90
|
-
}
|
|
91
|
-
if (TOOL_USAGE_ENABLED) {
|
|
92
|
-
console.log(`[tool-usage-capture] Saved ${newRecords.length} records for ${sessionKey}`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
catch (err) {
|
|
96
|
-
console.error("[tool-usage-capture] Failed to save tool usage:", err);
|
|
97
|
-
// Never throw - errors are logged only
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
// ============================================================================
|
|
101
|
-
// Hook Implementation
|
|
102
|
-
// ============================================================================
|
|
103
|
-
/**
|
|
104
|
-
* Main hook: captures tool usage automatically
|
|
105
|
-
* Safe: validates all inputs, never throws, handles all edge cases
|
|
106
|
-
*/
|
|
107
|
-
export const toolUsageCaptureHook = async (ctx) => {
|
|
108
|
-
// Feature flag check (safe exit)
|
|
109
|
-
if (!TOOL_USAGE_ENABLED) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
try {
|
|
113
|
-
// Validate inputs (safe defaults)
|
|
114
|
-
const toolName = ctx.toolName || "unknown_tool";
|
|
115
|
-
const executionTimeMs = ctx.executionTimeMs || 0;
|
|
116
|
-
const success = ctx.success !== undefined ? ctx.success : true;
|
|
117
|
-
// Create compressed record
|
|
118
|
-
const record = createToolUsageRecord(toolName, ctx.toolInput, ctx.toolOutput, executionTimeMs, success, ctx.error);
|
|
119
|
-
// Load existing usage
|
|
120
|
-
const usage = await loadSessionToolUsage(ctx.sessionKey);
|
|
121
|
-
// Update stats
|
|
122
|
-
if (!usage.tools[toolName]) {
|
|
123
|
-
usage.tools[toolName] = {
|
|
124
|
-
count: 0,
|
|
125
|
-
totalMs: 0,
|
|
126
|
-
avgMs: 0,
|
|
127
|
-
lastUsed: record.timestamp,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
usage.tools[toolName] = updateToolStats(usage.tools[toolName], executionTimeMs);
|
|
131
|
-
// Add to history (respects max limit)
|
|
132
|
-
usage.history = addToHistory(usage.history, record, TOOL_USAGE_MAX_RECORDS);
|
|
133
|
-
usage.lastCaptured = Date.now();
|
|
134
|
-
// Save (async, non-blocking)
|
|
135
|
-
await saveSessionToolUsage(ctx.sessionKey, usage).catch((err) => {
|
|
136
|
-
// Save errors are logged in saveSessionToolUsage
|
|
137
|
-
// This catch is just for extra safety
|
|
138
|
-
console.error("[tool-usage-capture] Unhandled save error:", err);
|
|
139
|
-
});
|
|
140
|
-
// Debug logging (can be disabled)
|
|
141
|
-
if (success && TOOL_USAGE_ENABLED) {
|
|
142
|
-
console.log(`[tool-usage-capture] ${toolName} (${executionTimeMs}ms) - ${ctx.sessionKey}`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
catch (err) {
|
|
146
|
-
// NEVER throw from hook - would break the tool call
|
|
147
|
-
console.error("[tool-usage-capture] Hook error:", err);
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
// ============================================================================
|
|
151
|
-
// Buffer Management (performance optimization)
|
|
152
|
-
// ============================================================================
|
|
153
|
-
/**
|
|
154
|
-
* Flush in-memory buffer to storage
|
|
155
|
-
* Safe: handles errors, never throws
|
|
156
|
-
*/
|
|
157
|
-
export async function flushToolUsageBuffer() {
|
|
158
|
-
try {
|
|
159
|
-
if (toolUsageBuffer.size === 0) {
|
|
160
|
-
return; // Nothing to flush
|
|
161
|
-
}
|
|
162
|
-
let flushed = 0;
|
|
163
|
-
for (const [sessionKey, records] of toolUsageBuffer.entries()) {
|
|
164
|
-
try {
|
|
165
|
-
// TODO: Actually persist to SessionEntry in Phase 4
|
|
166
|
-
// For now, buffer stays in-memory
|
|
167
|
-
flushed += records.length;
|
|
168
|
-
}
|
|
169
|
-
catch (err) {
|
|
170
|
-
console.error(`[tool-usage-capture] Failed to flush buffer for ${sessionKey}:`, err);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
if (flushed > 0 && TOOL_USAGE_ENABLED) {
|
|
174
|
-
console.log(`[tool-usage-capture] Flushed ${flushed} records from buffer`);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
catch (err) {
|
|
178
|
-
console.error("[tool-usage-capture] Buffer flush error:", err);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Schedule buffer flush
|
|
183
|
-
* Safe: debounced, only schedules once
|
|
184
|
-
*/
|
|
185
|
-
function scheduleBufferFlush() {
|
|
186
|
-
if (bufferFlushScheduled) {
|
|
187
|
-
return; // Already scheduled
|
|
188
|
-
}
|
|
189
|
-
bufferFlushScheduled = true;
|
|
190
|
-
// Flush after 30 seconds
|
|
191
|
-
setTimeout(async () => {
|
|
192
|
-
await flushToolUsageBuffer();
|
|
193
|
-
bufferFlushScheduled = false;
|
|
194
|
-
}, TOOL_USAGE_BUFFER_FLUSH_INTERVAL);
|
|
195
|
-
}
|
|
196
|
-
// ============================================================================
|
|
197
|
-
// Query API (for future use)
|
|
198
|
-
// ============================================================================
|
|
199
|
-
/**
|
|
200
|
-
* Get tool usage statistics for a session
|
|
201
|
-
* Safe: returns empty object if no data
|
|
202
|
-
*/
|
|
203
|
-
export async function getSessionToolUsage(sessionKey) {
|
|
204
|
-
try {
|
|
205
|
-
const usage = await loadSessionToolUsage(sessionKey);
|
|
206
|
-
return usage;
|
|
207
|
-
}
|
|
208
|
-
catch (err) {
|
|
209
|
-
console.error(`[tool-usage-capture] Failed to get usage for ${sessionKey}:`, err);
|
|
210
|
-
return undefined;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* Get recent tool usage (last N records)
|
|
215
|
-
* Safe: returns empty array if no data
|
|
216
|
-
*/
|
|
217
|
-
export async function getRecentToolUsage(sessionKey, count = 10) {
|
|
218
|
-
try {
|
|
219
|
-
const usage = await loadSessionToolUsage(sessionKey);
|
|
220
|
-
return usage.history.slice(-count);
|
|
221
|
-
}
|
|
222
|
-
catch (err) {
|
|
223
|
-
console.error(`[tool-usage-capture] Failed to get recent usage for ${sessionKey}:`, err);
|
|
224
|
-
return [];
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
// ============================================================================
|
|
228
|
-
// Cleanup
|
|
229
|
-
// ============================================================================
|
|
230
|
-
/**
|
|
231
|
-
* Clear tool usage buffer (for testing)
|
|
232
|
-
* Safe: never throws
|
|
233
|
-
*/
|
|
234
|
-
export function clearToolUsageBuffer() {
|
|
235
|
-
try {
|
|
236
|
-
toolUsageBuffer.clear();
|
|
237
|
-
}
|
|
238
|
-
catch (err) {
|
|
239
|
-
console.error("[tool-usage-capture] Buffer clear error:", err);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Clear tool usage for a specific session
|
|
244
|
-
* Safe: never throws
|
|
245
|
-
*/
|
|
246
|
-
export function clearSessionToolUsage(sessionKey) {
|
|
247
|
-
try {
|
|
248
|
-
toolUsageBuffer.delete(sessionKey);
|
|
249
|
-
}
|
|
250
|
-
catch (err) {
|
|
251
|
-
console.error(`[tool-usage-capture] Failed to clear usage for ${sessionKey}:`, err);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool Usage Capture - Storage Layer
|
|
3
|
-
*
|
|
4
|
-
* Safe, efficient storage for tool usage data.
|
|
5
|
-
* Phase 2a: Type definitions and interfaces (NO breaking changes)
|
|
6
|
-
*
|
|
7
|
-
* Safety measures:
|
|
8
|
-
* - All fields optional (backward compatible)
|
|
9
|
-
* - Version field for future migrations
|
|
10
|
-
* - No modifications to existing SessionEntry fields
|
|
11
|
-
*/
|
|
12
|
-
// ============================================================================
|
|
13
|
-
// Constants
|
|
14
|
-
// ============================================================================
|
|
15
|
-
export const TOOL_USAGE_MAX_RECORDS = 100; // Keep only last 100 records
|
|
16
|
-
export const TOOL_USAGE_MAX_SUMMARY_LEN = 100; // Max 100 chars per summary
|
|
17
|
-
export const TOOL_USAGE_ENABLED = true; // Feature flag (master switch)
|
|
18
|
-
// ============================================================================
|
|
19
|
-
// Compression Functions (safe, no side effects)
|
|
20
|
-
// ============================================================================
|
|
21
|
-
/**
|
|
22
|
-
* Compress a tool input/output value to a short summary
|
|
23
|
-
* Safe: never throws, handles all types gracefully
|
|
24
|
-
*/
|
|
25
|
-
export function compressToolValue(value, maxLen = TOOL_USAGE_MAX_SUMMARY_LEN) {
|
|
26
|
-
try {
|
|
27
|
-
// Handle primitives
|
|
28
|
-
if (value === null)
|
|
29
|
-
return "null";
|
|
30
|
-
if (value === undefined)
|
|
31
|
-
return "undefined";
|
|
32
|
-
if (typeof value === "string") {
|
|
33
|
-
return value.length > maxLen ? value.slice(0, maxLen) + "..." : value;
|
|
34
|
-
}
|
|
35
|
-
if (typeof value === "number")
|
|
36
|
-
return String(value);
|
|
37
|
-
if (typeof value === "boolean")
|
|
38
|
-
return String(value);
|
|
39
|
-
// Handle objects and arrays
|
|
40
|
-
if (typeof value === "object") {
|
|
41
|
-
const json = JSON.stringify(value);
|
|
42
|
-
return json.length > maxLen ? json.slice(0, maxLen) + "..." : json;
|
|
43
|
-
}
|
|
44
|
-
// Fallback
|
|
45
|
-
return String(value);
|
|
46
|
-
}
|
|
47
|
-
catch (err) {
|
|
48
|
-
// If compression fails, return safe fallback
|
|
49
|
-
return "[compression_error]";
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Create a tool usage record from hook context
|
|
54
|
-
* Safe: validates all inputs, never throws
|
|
55
|
-
*/
|
|
56
|
-
export function createToolUsageRecord(toolName, toolInput, toolOutput, executionTimeMs, success, error) {
|
|
57
|
-
// Validate inputs (safe defaults)
|
|
58
|
-
const safeToolName = typeof toolName === "string" && toolName.length > 0 ? toolName : "unknown_tool";
|
|
59
|
-
const safeInput = typeof toolInput === "object" && toolInput !== null ? toolInput : {};
|
|
60
|
-
const safeTime = typeof executionTimeMs === "number" && executionTimeMs >= 0 ? executionTimeMs : 0;
|
|
61
|
-
const safeSuccess = Boolean(success);
|
|
62
|
-
return {
|
|
63
|
-
toolName: safeToolName,
|
|
64
|
-
timestamp: Date.now(),
|
|
65
|
-
executionTimeMs: safeTime,
|
|
66
|
-
success: safeSuccess,
|
|
67
|
-
inputSummary: compressToolValue(safeInput),
|
|
68
|
-
outputSummary: compressToolValue(toolOutput),
|
|
69
|
-
error: error ? String(error).slice(0, 200) : undefined,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
// ============================================================================
|
|
73
|
-
// Statistics Update (pure function, no side effects)
|
|
74
|
-
// ============================================================================
|
|
75
|
-
/**
|
|
76
|
-
* Update tool statistics with a new record
|
|
77
|
-
* Safe: never mutates input, returns new object
|
|
78
|
-
*/
|
|
79
|
-
export function updateToolStats(existingStats, executionTimeMs) {
|
|
80
|
-
const count = (existingStats?.count ?? 0) + 1;
|
|
81
|
-
const totalMs = (existingStats?.totalMs ?? 0) + executionTimeMs;
|
|
82
|
-
const avgMs = count > 0 ? Math.round((totalMs / count) * 100) / 100 : 0;
|
|
83
|
-
return {
|
|
84
|
-
count,
|
|
85
|
-
totalMs,
|
|
86
|
-
avgMs,
|
|
87
|
-
lastUsed: Date.now(),
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
// ============================================================================
|
|
91
|
-
// History Management (pure functions)
|
|
92
|
-
// ============================================================================
|
|
93
|
-
/**
|
|
94
|
-
* Add record to history, respecting max limit
|
|
95
|
-
* Safe: never exceeds max records, returns new array
|
|
96
|
-
*/
|
|
97
|
-
export function addToHistory(history, record, maxRecords = TOOL_USAGE_MAX_RECORDS) {
|
|
98
|
-
const newHistory = [...history, record];
|
|
99
|
-
// Keep only last N records
|
|
100
|
-
if (newHistory.length > maxRecords) {
|
|
101
|
-
return newHistory.slice(-maxRecords);
|
|
102
|
-
}
|
|
103
|
-
return newHistory;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Get recent tool usage (last N records)
|
|
107
|
-
* Safe: never mutates input, returns slice
|
|
108
|
-
*/
|
|
109
|
-
export function getRecentToolUsage(history, count = 10) {
|
|
110
|
-
return history.slice(-count);
|
|
111
|
-
}
|
|
112
|
-
// ============================================================================
|
|
113
|
-
// Query Functions (for future use)
|
|
114
|
-
// ============================================================================
|
|
115
|
-
/**
|
|
116
|
-
* Get statistics for a specific tool
|
|
117
|
-
* Safe: returns undefined if tool not found
|
|
118
|
-
*/
|
|
119
|
-
export function getToolStats(usage, toolName) {
|
|
120
|
-
return usage?.tools[toolName];
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Get all tool names sorted by usage frequency
|
|
124
|
-
* Safe: returns empty array if no data
|
|
125
|
-
*/
|
|
126
|
-
export function getToolsByFrequency(usage) {
|
|
127
|
-
if (!usage?.tools)
|
|
128
|
-
return [];
|
|
129
|
-
return Object.entries(usage.tools)
|
|
130
|
-
.map(([toolName, stats]) => ({ toolName, count: stats.count }))
|
|
131
|
-
.sort((a, b) => b.count - a.count);
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Get slowest tools (by average execution time)
|
|
135
|
-
* Safe: returns empty array if no data
|
|
136
|
-
*/
|
|
137
|
-
export function getSlowestTools(usage, limit = 5) {
|
|
138
|
-
if (!usage?.tools)
|
|
139
|
-
return [];
|
|
140
|
-
return Object.entries(usage.tools)
|
|
141
|
-
.map(([toolName, stats]) => ({ toolName, avgMs: stats.avgMs }))
|
|
142
|
-
.sort((a, b) => b.avgMs - a.avgMs)
|
|
143
|
-
.slice(0, limit);
|
|
144
|
-
}
|