@hcu-lab.me/mcp-device 1.0.0
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/LICENSE +21 -0
- package/README.md +51 -0
- package/dist/bootstrap.d.ts +1 -0
- package/dist/bootstrap.js +22 -0
- package/dist/command-manager.d.ts +8 -0
- package/dist/command-manager.js +241 -0
- package/dist/config-field-definitions.d.ts +41 -0
- package/dist/config-field-definitions.js +37 -0
- package/dist/config-manager.d.ts +102 -0
- package/dist/config-manager.js +291 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.js +10 -0
- package/dist/custom-stdio.d.ts +51 -0
- package/dist/custom-stdio.js +358 -0
- package/dist/data/official-app-ca.pem +12 -0
- package/dist/data/onboarding-prompts.json +70 -0
- package/dist/device/device-state.d.ts +13 -0
- package/dist/device/device-state.js +100 -0
- package/dist/device/device-status.d.ts +81 -0
- package/dist/device/device-status.js +156 -0
- package/dist/device/device.d.ts +16 -0
- package/dist/device/device.js +190 -0
- package/dist/device/execution-engine.d.ts +144 -0
- package/dist/device/execution-engine.js +195 -0
- package/dist/device/gateway-channel.d.ts +48 -0
- package/dist/device/gateway-channel.js +384 -0
- package/dist/device/gateway-config.d.ts +40 -0
- package/dist/device/gateway-config.js +207 -0
- package/dist/device/gateway-identity.d.ts +35 -0
- package/dist/device/gateway-identity.js +178 -0
- package/dist/device/gateway-pairing.d.ts +25 -0
- package/dist/device/gateway-pairing.js +105 -0
- package/dist/device/gateway-secure-transport.d.ts +25 -0
- package/dist/device/gateway-secure-transport.js +117 -0
- package/dist/device/gateway-tool-adapter.d.ts +17 -0
- package/dist/device/gateway-tool-adapter.js +284 -0
- package/dist/device/gateway-url-policy.d.ts +3 -0
- package/dist/device/gateway-url-policy.js +43 -0
- package/dist/device/linux-service.d.ts +70 -0
- package/dist/device/linux-service.js +268 -0
- package/dist/device/official-trust.d.ts +7 -0
- package/dist/device/official-trust.js +49 -0
- package/dist/device/project-inspection.d.ts +1 -0
- package/dist/device/project-inspection.js +180 -0
- package/dist/device/runtime-owner.d.ts +41 -0
- package/dist/device/runtime-owner.js +177 -0
- package/dist/device/windows-dpapi.d.ts +11 -0
- package/dist/device/windows-dpapi.js +67 -0
- package/dist/device/windows-service.d.ts +56 -0
- package/dist/device/windows-service.js +238 -0
- package/dist/error-handlers.d.ts +7 -0
- package/dist/error-handlers.js +15 -0
- package/dist/handlers/edit-search-handlers.d.ts +6 -0
- package/dist/handlers/edit-search-handlers.js +6 -0
- package/dist/handlers/filesystem-handlers.d.ts +38 -0
- package/dist/handlers/filesystem-handlers.js +440 -0
- package/dist/handlers/history-handlers.d.ts +12 -0
- package/dist/handlers/history-handlers.js +67 -0
- package/dist/handlers/index.d.ts +6 -0
- package/dist/handlers/index.js +7 -0
- package/dist/handlers/process-handlers.d.ts +9 -0
- package/dist/handlers/process-handlers.js +15 -0
- package/dist/handlers/search-handlers.d.ts +17 -0
- package/dist/handlers/search-handlers.js +224 -0
- package/dist/handlers/terminal-handlers.d.ts +21 -0
- package/dist/handlers/terminal-handlers.js +35 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +138 -0
- package/dist/mcp-device.d.ts +2 -0
- package/dist/mcp-device.js +7 -0
- package/dist/npm-scripts/remote.d.ts +3 -0
- package/dist/npm-scripts/remote.js +314 -0
- package/dist/search-manager.d.ts +147 -0
- package/dist/search-manager.js +828 -0
- package/dist/server.d.ts +46 -0
- package/dist/server.js +1563 -0
- package/dist/terminal-manager.d.ts +104 -0
- package/dist/terminal-manager.js +635 -0
- package/dist/tools/config.d.ts +96 -0
- package/dist/tools/config.js +260 -0
- package/dist/tools/edit.d.ts +38 -0
- package/dist/tools/edit.js +435 -0
- package/dist/tools/feedback.d.ts +8 -0
- package/dist/tools/feedback.js +132 -0
- package/dist/tools/filesystem.d.ts +79 -0
- package/dist/tools/filesystem.js +947 -0
- package/dist/tools/fuzzySearch.d.ts +12 -0
- package/dist/tools/fuzzySearch.js +74 -0
- package/dist/tools/fuzzySearchCore.d.ts +52 -0
- package/dist/tools/fuzzySearchCore.js +125 -0
- package/dist/tools/improved-process-tools.d.ts +24 -0
- package/dist/tools/improved-process-tools.js +627 -0
- package/dist/tools/mime-types.d.ts +3 -0
- package/dist/tools/mime-types.js +28 -0
- package/dist/tools/pdf/extract-images.d.ts +34 -0
- package/dist/tools/pdf/extract-images.js +132 -0
- package/dist/tools/pdf/index.d.ts +6 -0
- package/dist/tools/pdf/index.js +3 -0
- package/dist/tools/pdf/lib/pdf2md.d.ts +36 -0
- package/dist/tools/pdf/lib/pdf2md.js +76 -0
- package/dist/tools/pdf/manipulations.d.ts +13 -0
- package/dist/tools/pdf/manipulations.js +99 -0
- package/dist/tools/pdf/markdown.d.ts +26 -0
- package/dist/tools/pdf/markdown.js +275 -0
- package/dist/tools/pdf/utils.d.ts +12 -0
- package/dist/tools/pdf/utils.js +34 -0
- package/dist/tools/process.d.ts +3 -0
- package/dist/tools/process.js +56 -0
- package/dist/tools/prompts.d.ts +28 -0
- package/dist/tools/prompts.js +259 -0
- package/dist/tools/schemas.d.ts +454 -0
- package/dist/tools/schemas.js +242 -0
- package/dist/tools/usage.d.ts +5 -0
- package/dist/tools/usage.js +24 -0
- package/dist/types.d.ts +83 -0
- package/dist/types.js +1 -0
- package/dist/ui/config-editor/config-editor-runtime.js +150 -0
- package/dist/ui/config-editor/index.html +13 -0
- package/dist/ui/config-editor/src/app.d.ts +43 -0
- package/dist/ui/config-editor/src/app.js +831 -0
- package/dist/ui/config-editor/src/array-modal.d.ts +19 -0
- package/dist/ui/config-editor/src/array-modal.js +185 -0
- package/dist/ui/config-editor/src/main.d.ts +1 -0
- package/dist/ui/config-editor/src/main.js +2 -0
- package/dist/ui/config-editor/styles.css +587 -0
- package/dist/ui/contracts.d.ts +14 -0
- package/dist/ui/contracts.js +21 -0
- package/dist/ui/file-preview/index.html +16 -0
- package/dist/ui/file-preview/preview-runtime.js +469 -0
- package/dist/ui/file-preview/shared/preview-file-types.d.ts +5 -0
- package/dist/ui/file-preview/shared/preview-file-types.js +59 -0
- package/dist/ui/file-preview/src/app.d.ts +4 -0
- package/dist/ui/file-preview/src/app.js +680 -0
- package/dist/ui/file-preview/src/components/code-viewer.d.ts +6 -0
- package/dist/ui/file-preview/src/components/code-viewer.js +73 -0
- package/dist/ui/file-preview/src/components/highlighting.d.ts +2 -0
- package/dist/ui/file-preview/src/components/highlighting.js +54 -0
- package/dist/ui/file-preview/src/components/html-renderer.d.ts +5 -0
- package/dist/ui/file-preview/src/components/html-renderer.js +47 -0
- package/dist/ui/file-preview/src/components/markdown-renderer.d.ts +1 -0
- package/dist/ui/file-preview/src/components/markdown-renderer.js +59 -0
- package/dist/ui/file-preview/src/directory-controller.d.ts +8 -0
- package/dist/ui/file-preview/src/directory-controller.js +240 -0
- package/dist/ui/file-preview/src/document-layout.d.ts +20 -0
- package/dist/ui/file-preview/src/document-layout.js +109 -0
- package/dist/ui/file-preview/src/document-outline.d.ts +17 -0
- package/dist/ui/file-preview/src/document-outline.js +97 -0
- package/dist/ui/file-preview/src/document-workspace.d.ts +19 -0
- package/dist/ui/file-preview/src/document-workspace.js +33 -0
- package/dist/ui/file-preview/src/file-type-handlers.d.ts +10 -0
- package/dist/ui/file-preview/src/file-type-handlers.js +109 -0
- package/dist/ui/file-preview/src/host/external-actions.d.ts +8 -0
- package/dist/ui/file-preview/src/host/external-actions.js +55 -0
- package/dist/ui/file-preview/src/host/selection-context.d.ts +9 -0
- package/dist/ui/file-preview/src/host/selection-context.js +106 -0
- package/dist/ui/file-preview/src/image-preview.d.ts +3 -0
- package/dist/ui/file-preview/src/image-preview.js +21 -0
- package/dist/ui/file-preview/src/main.d.ts +1 -0
- package/dist/ui/file-preview/src/main.js +5 -0
- package/dist/ui/file-preview/src/markdown/conflict-dialog.d.ts +40 -0
- package/dist/ui/file-preview/src/markdown/conflict-dialog.js +163 -0
- package/dist/ui/file-preview/src/markdown/controller.d.ts +44 -0
- package/dist/ui/file-preview/src/markdown/controller.js +1043 -0
- package/dist/ui/file-preview/src/markdown/editor.d.ts +131 -0
- package/dist/ui/file-preview/src/markdown/editor.js +1479 -0
- package/dist/ui/file-preview/src/markdown/linking.d.ts +16 -0
- package/dist/ui/file-preview/src/markdown/linking.js +228 -0
- package/dist/ui/file-preview/src/markdown/outline.d.ts +2 -0
- package/dist/ui/file-preview/src/markdown/outline.js +16 -0
- package/dist/ui/file-preview/src/markdown/parser.d.ts +30 -0
- package/dist/ui/file-preview/src/markdown/parser.js +38 -0
- package/dist/ui/file-preview/src/markdown/preview.d.ts +1 -0
- package/dist/ui/file-preview/src/markdown/preview.js +20 -0
- package/dist/ui/file-preview/src/markdown/slugify.d.ts +3 -0
- package/dist/ui/file-preview/src/markdown/slugify.js +31 -0
- package/dist/ui/file-preview/src/markdown/utils.d.ts +1 -0
- package/dist/ui/file-preview/src/markdown/utils.js +15 -0
- package/dist/ui/file-preview/src/model.d.ts +35 -0
- package/dist/ui/file-preview/src/model.js +1 -0
- package/dist/ui/file-preview/src/panel-actions.d.ts +17 -0
- package/dist/ui/file-preview/src/panel-actions.js +182 -0
- package/dist/ui/file-preview/src/path-utils.d.ts +6 -0
- package/dist/ui/file-preview/src/path-utils.js +64 -0
- package/dist/ui/file-preview/src/payload-utils.d.ts +11 -0
- package/dist/ui/file-preview/src/payload-utils.js +119 -0
- package/dist/ui/file-preview/src/types.d.ts +1 -0
- package/dist/ui/file-preview/src/types.js +1 -0
- package/dist/ui/file-preview/styles.css +1631 -0
- package/dist/ui/resources.d.ts +28 -0
- package/dist/ui/resources.js +86 -0
- package/dist/ui/shared/compact-row.d.ts +11 -0
- package/dist/ui/shared/compact-row.js +18 -0
- package/dist/ui/shared/escape-html.d.ts +4 -0
- package/dist/ui/shared/escape-html.js +11 -0
- package/dist/ui/shared/host-context.d.ts +15 -0
- package/dist/ui/shared/host-context.js +51 -0
- package/dist/ui/shared/tool-bridge.d.ts +30 -0
- package/dist/ui/shared/tool-bridge.js +137 -0
- package/dist/ui/shared/tool-shell.d.ts +25 -0
- package/dist/ui/shared/tool-shell.js +107 -0
- package/dist/ui/shared/ui-event-tracker.d.ts +9 -0
- package/dist/ui/shared/ui-event-tracker.js +27 -0
- package/dist/ui/shared/widget-state.d.ts +33 -0
- package/dist/ui/shared/widget-state.js +158 -0
- package/dist/utils/ab-test.d.ts +12 -0
- package/dist/utils/ab-test.js +104 -0
- package/dist/utils/capture.d.ts +35 -0
- package/dist/utils/capture.js +481 -0
- package/dist/utils/dockerPrompt.d.ts +17 -0
- package/dist/utils/dockerPrompt.js +53 -0
- package/dist/utils/feature-flags.d.ts +59 -0
- package/dist/utils/feature-flags.js +215 -0
- package/dist/utils/files/base.d.ts +173 -0
- package/dist/utils/files/base.js +5 -0
- package/dist/utils/files/binary.d.ts +21 -0
- package/dist/utils/files/binary.js +65 -0
- package/dist/utils/files/docx.d.ts +47 -0
- package/dist/utils/files/docx.js +679 -0
- package/dist/utils/files/excel.d.ts +24 -0
- package/dist/utils/files/excel.js +437 -0
- package/dist/utils/files/factory.d.ts +41 -0
- package/dist/utils/files/factory.js +113 -0
- package/dist/utils/files/image.d.ts +21 -0
- package/dist/utils/files/image.js +78 -0
- package/dist/utils/files/index.d.ts +10 -0
- package/dist/utils/files/index.js +13 -0
- package/dist/utils/files/pdf.d.ts +32 -0
- package/dist/utils/files/pdf.js +142 -0
- package/dist/utils/files/text.d.ts +63 -0
- package/dist/utils/files/text.js +370 -0
- package/dist/utils/fuzzySearchLogger.d.ts +30 -0
- package/dist/utils/fuzzySearchLogger.js +126 -0
- package/dist/utils/lineEndingHandler.d.ts +21 -0
- package/dist/utils/lineEndingHandler.js +77 -0
- package/dist/utils/logger.d.ts +32 -0
- package/dist/utils/logger.js +72 -0
- package/dist/utils/mcp-ui-ab-test.d.ts +13 -0
- package/dist/utils/mcp-ui-ab-test.js +62 -0
- package/dist/utils/open-browser.d.ts +9 -0
- package/dist/utils/open-browser.js +42 -0
- package/dist/utils/process-detection.d.ts +23 -0
- package/dist/utils/process-detection.js +150 -0
- package/dist/utils/ripgrep-resolver.d.ts +9 -0
- package/dist/utils/ripgrep-resolver.js +74 -0
- package/dist/utils/system-info.d.ts +71 -0
- package/dist/utils/system-info.js +712 -0
- package/dist/utils/toolHistory.d.ts +86 -0
- package/dist/utils/toolHistory.js +257 -0
- package/dist/utils/trackTools.d.ts +6 -0
- package/dist/utils/trackTools.js +54 -0
- package/dist/utils/unsupportedParams.d.ts +24 -0
- package/dist/utils/unsupportedParams.js +66 -0
- package/dist/utils/usageTracker.d.ts +133 -0
- package/dist/utils/usageTracker.js +477 -0
- package/dist/utils/welcome-onboarding.d.ts +11 -0
- package/dist/utils/welcome-onboarding.js +99 -0
- package/dist/utils/withTimeout.d.ts +28 -0
- package/dist/utils/withTimeout.js +85 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +131 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { ServerResult } from '../types.js';
|
|
2
|
+
export interface ToolCallRecord {
|
|
3
|
+
timestamp: string;
|
|
4
|
+
toolName: string;
|
|
5
|
+
arguments: any;
|
|
6
|
+
output: ServerResult;
|
|
7
|
+
duration?: number;
|
|
8
|
+
}
|
|
9
|
+
interface FormattedToolCallRecord extends Omit<ToolCallRecord, 'timestamp'> {
|
|
10
|
+
timestamp: string;
|
|
11
|
+
}
|
|
12
|
+
declare class ToolHistory {
|
|
13
|
+
private history;
|
|
14
|
+
private readonly MAX_ENTRIES;
|
|
15
|
+
private readonly MAX_HISTORY_FILE_SIZE_BYTES;
|
|
16
|
+
private readonly HISTORY_FILE_TRIM_TARGET_BYTES;
|
|
17
|
+
private readonly historyFile;
|
|
18
|
+
private writeQueue;
|
|
19
|
+
private isWriting;
|
|
20
|
+
private writeInterval?;
|
|
21
|
+
constructor();
|
|
22
|
+
/**
|
|
23
|
+
* Load history from disk (all instances share the same file)
|
|
24
|
+
*/
|
|
25
|
+
private loadFromDisk;
|
|
26
|
+
/**
|
|
27
|
+
* Trim the on-disk history file to stay under the size cap by dropping the
|
|
28
|
+
* oldest entries (lines) until the kept tail fits within the trim target.
|
|
29
|
+
* Returns true only when the file was actually rewritten with a smaller
|
|
30
|
+
* tail, so callers can fall through to their normal path on failure or
|
|
31
|
+
* no-op rather than mutating in-memory state.
|
|
32
|
+
*
|
|
33
|
+
* Always keeps at least the most recent entry, even if a single record
|
|
34
|
+
* exceeds the trim target — there is no useful state below that.
|
|
35
|
+
*/
|
|
36
|
+
private trimHistoryFileIfTooLarge;
|
|
37
|
+
/**
|
|
38
|
+
* Trim history file to prevent it from growing indefinitely
|
|
39
|
+
*/
|
|
40
|
+
private trimHistoryFile;
|
|
41
|
+
/**
|
|
42
|
+
* Async write processor - batches writes to avoid blocking
|
|
43
|
+
*/
|
|
44
|
+
private startWriteProcessor;
|
|
45
|
+
/**
|
|
46
|
+
* Flush queued writes to disk
|
|
47
|
+
*/
|
|
48
|
+
private flushToDisk;
|
|
49
|
+
/**
|
|
50
|
+
* Add a tool call to history
|
|
51
|
+
*/
|
|
52
|
+
addCall(toolName: string, args: any, output: ServerResult, duration?: number): void;
|
|
53
|
+
/**
|
|
54
|
+
* Get recent tool calls with filters
|
|
55
|
+
*/
|
|
56
|
+
getRecentCalls(options: {
|
|
57
|
+
maxResults?: number;
|
|
58
|
+
toolName?: string;
|
|
59
|
+
since?: string;
|
|
60
|
+
}): ToolCallRecord[];
|
|
61
|
+
/**
|
|
62
|
+
* Get recent calls formatted with local timezone
|
|
63
|
+
*/
|
|
64
|
+
getRecentCallsFormatted(options: {
|
|
65
|
+
maxResults?: number;
|
|
66
|
+
toolName?: string;
|
|
67
|
+
since?: string;
|
|
68
|
+
}): FormattedToolCallRecord[];
|
|
69
|
+
/**
|
|
70
|
+
* Get current stats
|
|
71
|
+
*/
|
|
72
|
+
getStats(): {
|
|
73
|
+
totalEntries: number;
|
|
74
|
+
oldestEntry: string;
|
|
75
|
+
newestEntry: string;
|
|
76
|
+
historyFile: string;
|
|
77
|
+
queuedWrites: number;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Cleanup method - clears interval and flushes pending writes
|
|
81
|
+
* Call this during shutdown or in tests
|
|
82
|
+
*/
|
|
83
|
+
cleanup(): Promise<void>;
|
|
84
|
+
}
|
|
85
|
+
export declare const toolHistory: ToolHistory;
|
|
86
|
+
export {};
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { CONFIG_DIR } from '../config.js';
|
|
4
|
+
// Format timestamp in local timezone for display
|
|
5
|
+
function formatLocalTimestamp(isoTimestamp) {
|
|
6
|
+
const date = new Date(isoTimestamp);
|
|
7
|
+
return date.toLocaleString('en-US', {
|
|
8
|
+
year: 'numeric',
|
|
9
|
+
month: '2-digit',
|
|
10
|
+
day: '2-digit',
|
|
11
|
+
hour: '2-digit',
|
|
12
|
+
minute: '2-digit',
|
|
13
|
+
second: '2-digit',
|
|
14
|
+
hour12: false
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
class ToolHistory {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.history = [];
|
|
20
|
+
this.MAX_ENTRIES = 1000;
|
|
21
|
+
this.MAX_HISTORY_FILE_SIZE_BYTES = 5 * 1024 * 1024;
|
|
22
|
+
// When the file exceeds the cap we trim it down to this target instead of
|
|
23
|
+
// all the way to zero, so a single overflow doesn't cause every subsequent
|
|
24
|
+
// flush to re-trim.
|
|
25
|
+
this.HISTORY_FILE_TRIM_TARGET_BYTES = 4 * 1024 * 1024;
|
|
26
|
+
this.writeQueue = [];
|
|
27
|
+
this.isWriting = false;
|
|
28
|
+
// Store history under the canonical MCP Device state root.
|
|
29
|
+
const historyDir = path.join(CONFIG_DIR, 'logs');
|
|
30
|
+
// Ensure directory exists
|
|
31
|
+
if (!fs.existsSync(historyDir)) {
|
|
32
|
+
fs.mkdirSync(historyDir, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
// Use append-only JSONL format (JSON Lines).
|
|
35
|
+
this.historyFile = path.join(historyDir, 'history.jsonl');
|
|
36
|
+
// Load existing history on startup
|
|
37
|
+
this.loadFromDisk();
|
|
38
|
+
// Start async write processor
|
|
39
|
+
this.startWriteProcessor();
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Load history from disk (all instances share the same file)
|
|
43
|
+
*/
|
|
44
|
+
loadFromDisk() {
|
|
45
|
+
try {
|
|
46
|
+
if (!fs.existsSync(this.historyFile)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// If the file is over the cap, trim it down before reading so we
|
|
50
|
+
// load a bounded amount.
|
|
51
|
+
this.trimHistoryFileIfTooLarge();
|
|
52
|
+
const content = fs.readFileSync(this.historyFile, 'utf-8');
|
|
53
|
+
const lines = content.trim().split('\n').filter(line => line.trim());
|
|
54
|
+
// Parse each line as JSON
|
|
55
|
+
const records = [];
|
|
56
|
+
for (const line of lines) {
|
|
57
|
+
try {
|
|
58
|
+
records.push(JSON.parse(line));
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
// Silently skip invalid lines
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Keep only last 1000 entries
|
|
65
|
+
this.history = records.slice(-this.MAX_ENTRIES);
|
|
66
|
+
// If file is getting too large, trim it
|
|
67
|
+
if (lines.length > this.MAX_ENTRIES * 2) {
|
|
68
|
+
this.trimHistoryFile();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
// Silently fail
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Trim the on-disk history file to stay under the size cap by dropping the
|
|
77
|
+
* oldest entries (lines) until the kept tail fits within the trim target.
|
|
78
|
+
* Returns true only when the file was actually rewritten with a smaller
|
|
79
|
+
* tail, so callers can fall through to their normal path on failure or
|
|
80
|
+
* no-op rather than mutating in-memory state.
|
|
81
|
+
*
|
|
82
|
+
* Always keeps at least the most recent entry, even if a single record
|
|
83
|
+
* exceeds the trim target — there is no useful state below that.
|
|
84
|
+
*/
|
|
85
|
+
trimHistoryFileIfTooLarge() {
|
|
86
|
+
let stats;
|
|
87
|
+
try {
|
|
88
|
+
if (!fs.existsSync(this.historyFile)) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
stats = fs.statSync(this.historyFile);
|
|
92
|
+
if (stats.size <= this.MAX_HISTORY_FILE_SIZE_BYTES) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const content = fs.readFileSync(this.historyFile, 'utf-8');
|
|
101
|
+
const lines = content.split('\n').filter(line => line.length > 0);
|
|
102
|
+
if (lines.length === 0) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
// Walk lines from newest to oldest, accumulating bytes (line + '\n'),
|
|
106
|
+
// and keep as many as fit within the trim target. Always keep at
|
|
107
|
+
// least the last line.
|
|
108
|
+
const kept = [];
|
|
109
|
+
let bytes = 0;
|
|
110
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
111
|
+
const lineBytes = Buffer.byteLength(lines[i], 'utf-8') + 1; // +1 for '\n'
|
|
112
|
+
if (kept.length > 0 && bytes + lineBytes > this.HISTORY_FILE_TRIM_TARGET_BYTES) {
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
kept.push(lines[i]);
|
|
116
|
+
bytes += lineBytes;
|
|
117
|
+
}
|
|
118
|
+
kept.reverse();
|
|
119
|
+
fs.writeFileSync(this.historyFile, kept.join('\n') + '\n', 'utf-8');
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
// Trim failed; do not claim the file was changed.
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Trim history file to prevent it from growing indefinitely
|
|
129
|
+
*/
|
|
130
|
+
trimHistoryFile() {
|
|
131
|
+
try {
|
|
132
|
+
// Keep last 1000 entries in memory
|
|
133
|
+
const keepEntries = this.history.slice(-this.MAX_ENTRIES);
|
|
134
|
+
// Write them back
|
|
135
|
+
const lines = keepEntries.map(entry => JSON.stringify(entry)).join('\n') + '\n';
|
|
136
|
+
fs.writeFileSync(this.historyFile, lines, 'utf-8');
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
// Silently fail
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Async write processor - batches writes to avoid blocking
|
|
144
|
+
*/
|
|
145
|
+
startWriteProcessor() {
|
|
146
|
+
this.writeInterval = setInterval(() => {
|
|
147
|
+
if (this.writeQueue.length > 0 && !this.isWriting) {
|
|
148
|
+
this.flushToDisk();
|
|
149
|
+
}
|
|
150
|
+
}, 1000); // Flush every second
|
|
151
|
+
// Prevent interval from keeping process alive during shutdown/tests
|
|
152
|
+
this.writeInterval.unref();
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Flush queued writes to disk
|
|
156
|
+
*/
|
|
157
|
+
async flushToDisk() {
|
|
158
|
+
if (this.isWriting || this.writeQueue.length === 0)
|
|
159
|
+
return;
|
|
160
|
+
this.isWriting = true;
|
|
161
|
+
const toWrite = [...this.writeQueue];
|
|
162
|
+
this.writeQueue = [];
|
|
163
|
+
try {
|
|
164
|
+
// If the on-disk file has grown past the cap, trim it down to the
|
|
165
|
+
// target size (keeping the most recent entries) before appending.
|
|
166
|
+
// The in-memory cache is unaffected — it is already bounded by
|
|
167
|
+
// MAX_ENTRIES via addCall.
|
|
168
|
+
this.trimHistoryFileIfTooLarge();
|
|
169
|
+
// Append to file (atomic append operation)
|
|
170
|
+
const lines = toWrite.map(entry => JSON.stringify(entry)).join('\n') + '\n';
|
|
171
|
+
fs.appendFileSync(this.historyFile, lines, 'utf-8');
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
// Put back in queue on failure
|
|
175
|
+
this.writeQueue.unshift(...toWrite);
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
this.isWriting = false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Add a tool call to history
|
|
183
|
+
*/
|
|
184
|
+
addCall(toolName, args, output, duration) {
|
|
185
|
+
const record = {
|
|
186
|
+
timestamp: new Date().toISOString(),
|
|
187
|
+
toolName,
|
|
188
|
+
arguments: args,
|
|
189
|
+
output,
|
|
190
|
+
duration
|
|
191
|
+
};
|
|
192
|
+
this.history.push(record);
|
|
193
|
+
// Keep only last 1000 in memory
|
|
194
|
+
if (this.history.length > this.MAX_ENTRIES) {
|
|
195
|
+
this.history.shift();
|
|
196
|
+
}
|
|
197
|
+
// Queue for async write
|
|
198
|
+
this.writeQueue.push(record);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Get recent tool calls with filters
|
|
202
|
+
*/
|
|
203
|
+
getRecentCalls(options) {
|
|
204
|
+
let results = [...this.history];
|
|
205
|
+
// Filter by tool name
|
|
206
|
+
if (options.toolName) {
|
|
207
|
+
results = results.filter(r => r.toolName === options.toolName);
|
|
208
|
+
}
|
|
209
|
+
// Filter by timestamp
|
|
210
|
+
if (options.since) {
|
|
211
|
+
const sinceDate = new Date(options.since);
|
|
212
|
+
results = results.filter(r => new Date(r.timestamp) >= sinceDate);
|
|
213
|
+
}
|
|
214
|
+
// Limit results (default 50, max 1000)
|
|
215
|
+
const limit = Math.min(options.maxResults || 50, 1000);
|
|
216
|
+
return results.slice(-limit);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Get recent calls formatted with local timezone
|
|
220
|
+
*/
|
|
221
|
+
getRecentCallsFormatted(options) {
|
|
222
|
+
const calls = this.getRecentCalls(options);
|
|
223
|
+
// Format timestamps to local timezone
|
|
224
|
+
return calls.map(call => ({
|
|
225
|
+
...call,
|
|
226
|
+
timestamp: formatLocalTimestamp(call.timestamp)
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get current stats
|
|
231
|
+
*/
|
|
232
|
+
getStats() {
|
|
233
|
+
return {
|
|
234
|
+
totalEntries: this.history.length,
|
|
235
|
+
oldestEntry: this.history[0]?.timestamp,
|
|
236
|
+
newestEntry: this.history[this.history.length - 1]?.timestamp,
|
|
237
|
+
historyFile: this.historyFile,
|
|
238
|
+
queuedWrites: this.writeQueue.length
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Cleanup method - clears interval and flushes pending writes
|
|
243
|
+
* Call this during shutdown or in tests
|
|
244
|
+
*/
|
|
245
|
+
async cleanup() {
|
|
246
|
+
// Clear the interval
|
|
247
|
+
if (this.writeInterval) {
|
|
248
|
+
clearInterval(this.writeInterval);
|
|
249
|
+
this.writeInterval = undefined;
|
|
250
|
+
}
|
|
251
|
+
// Flush any remaining writes
|
|
252
|
+
if (this.writeQueue.length > 0) {
|
|
253
|
+
await this.flushToDisk();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
export const toolHistory = new ToolHistory();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { TOOL_CALL_FILE, TOOL_CALL_FILE_MAX_SIZE } from '../config.js';
|
|
4
|
+
// Ensure the directory for the log file exists
|
|
5
|
+
const logDir = path.dirname(TOOL_CALL_FILE);
|
|
6
|
+
await fs.promises.mkdir(logDir, { recursive: true });
|
|
7
|
+
/**
|
|
8
|
+
* Track tool calls and save them to a log file
|
|
9
|
+
* @param toolName Name of the tool being called
|
|
10
|
+
* @param args Arguments passed to the tool (optional)
|
|
11
|
+
*/
|
|
12
|
+
export async function trackToolCall(toolName, args) {
|
|
13
|
+
try {
|
|
14
|
+
// Get current timestamp
|
|
15
|
+
const timestamp = new Date().toISOString();
|
|
16
|
+
// Format the log entry
|
|
17
|
+
const logEntry = `${timestamp} | ${toolName.padEnd(20, ' ')}${args ? `\t| Arguments: ${JSON.stringify(args)}` : ''}\n`;
|
|
18
|
+
// Check if file exists and get its size
|
|
19
|
+
let fileSize = 0;
|
|
20
|
+
try {
|
|
21
|
+
const stats = await fs.promises.stat(TOOL_CALL_FILE);
|
|
22
|
+
fileSize = stats.size;
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
// File doesn't exist yet, size remains 0
|
|
26
|
+
}
|
|
27
|
+
// If file size is 10MB or larger, rotate the log file
|
|
28
|
+
if (fileSize >= TOOL_CALL_FILE_MAX_SIZE) {
|
|
29
|
+
const fileExt = path.extname(TOOL_CALL_FILE);
|
|
30
|
+
const fileBase = path.basename(TOOL_CALL_FILE, fileExt);
|
|
31
|
+
const dirName = path.dirname(TOOL_CALL_FILE);
|
|
32
|
+
// Create a timestamp-based filename for the old log
|
|
33
|
+
const date = new Date();
|
|
34
|
+
const rotateTimestamp = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}_${String(date.getHours()).padStart(2, '0')}-${String(date.getMinutes()).padStart(2, '0')}-${String(date.getSeconds()).padStart(2, '0')}`;
|
|
35
|
+
const newFileName = path.join(dirName, `${fileBase}_${rotateTimestamp}${fileExt}`);
|
|
36
|
+
// Rename the current file
|
|
37
|
+
await fs.promises.rename(TOOL_CALL_FILE, newFileName);
|
|
38
|
+
}
|
|
39
|
+
// Append to log file (if file was renamed, this will create a new file)
|
|
40
|
+
await fs.promises.appendFile(TOOL_CALL_FILE, logEntry, 'utf8');
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
44
|
+
const { capture } = await import('./capture.js');
|
|
45
|
+
// Send a final telemetry event noting that the user has opted out
|
|
46
|
+
// This helps us track opt-out rates while respecting the user's choice
|
|
47
|
+
await capture('server_track_tool_call_error', {
|
|
48
|
+
error: errorMessage,
|
|
49
|
+
toolName
|
|
50
|
+
});
|
|
51
|
+
// Don't let logging errors affect the main functionality
|
|
52
|
+
console.error(`Error logging tool call: ${error instanceof Error ? error.message : String(error)}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility for detecting parameters a caller sent that a tool's Zod schema does
|
|
3
|
+
* not accept.
|
|
4
|
+
*
|
|
5
|
+
* Zod's default `z.object()` silently strips unknown keys, so a model that
|
|
6
|
+
* invents a parameter (e.g. `view_range`) gets a normal-looking response with no
|
|
7
|
+
* signal that its input was ignored. These helpers let the dispatcher surface a
|
|
8
|
+
* corrective warning instead.
|
|
9
|
+
*
|
|
10
|
+
* Detection is top-level only. That is sufficient here because none of the tool
|
|
11
|
+
* arg schemas use a top-level `.passthrough()`/catchall — free-form fields
|
|
12
|
+
* (`options`, `params`, `pdfOptions`) are named keys whose contents are
|
|
13
|
+
* intentionally unvalidated.
|
|
14
|
+
*/
|
|
15
|
+
/** List the parameter names a tool schema accepts (empty array if it takes none). */
|
|
16
|
+
export declare function getSupportedParams(schema: unknown): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Return the names of top-level keys in `args` that the schema does not accept.
|
|
19
|
+
* Returns [] when args is not a plain object or the schema's params can't be
|
|
20
|
+
* determined (so we never warn on something we can't reason about).
|
|
21
|
+
*/
|
|
22
|
+
export declare function detectUnsupportedParams(args: unknown, schema: unknown): string[];
|
|
23
|
+
/** Build the corrective warning string shown to the model. */
|
|
24
|
+
export declare function buildUnsupportedParamsWarning(toolName: string, unsupported: string[], supported: string[]): string;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility for detecting parameters a caller sent that a tool's Zod schema does
|
|
3
|
+
* not accept.
|
|
4
|
+
*
|
|
5
|
+
* Zod's default `z.object()` silently strips unknown keys, so a model that
|
|
6
|
+
* invents a parameter (e.g. `view_range`) gets a normal-looking response with no
|
|
7
|
+
* signal that its input was ignored. These helpers let the dispatcher surface a
|
|
8
|
+
* corrective warning instead.
|
|
9
|
+
*
|
|
10
|
+
* Detection is top-level only. That is sufficient here because none of the tool
|
|
11
|
+
* arg schemas use a top-level `.passthrough()`/catchall — free-form fields
|
|
12
|
+
* (`options`, `params`, `pdfOptions`) are named keys whose contents are
|
|
13
|
+
* intentionally unvalidated.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Resolve a Zod schema down to the field map of its underlying object schema,
|
|
17
|
+
* unwrapping wrappers (effects/optional/default/nullable/branded) defensively.
|
|
18
|
+
* Returns null when the schema is not (and does not wrap) a plain object schema,
|
|
19
|
+
* in which case we cannot determine the supported parameters and must not warn.
|
|
20
|
+
*/
|
|
21
|
+
function getObjectShape(schema) {
|
|
22
|
+
let current = schema;
|
|
23
|
+
for (let i = 0; current && current._def && i < 10; i++) {
|
|
24
|
+
const typeName = current._def.typeName;
|
|
25
|
+
if (typeName === 'ZodObject') {
|
|
26
|
+
const shape = typeof current.shape === 'function' ? current.shape() : current.shape;
|
|
27
|
+
return shape ?? null;
|
|
28
|
+
}
|
|
29
|
+
if (typeName === 'ZodEffects')
|
|
30
|
+
current = current._def.schema;
|
|
31
|
+
else if (typeName === 'ZodOptional' || typeName === 'ZodNullable' || typeName === 'ZodDefault') {
|
|
32
|
+
current = typeof current._def.innerType === 'function' ? current._def.innerType() : current._def.innerType;
|
|
33
|
+
}
|
|
34
|
+
else if (typeName === 'ZodBranded')
|
|
35
|
+
current = current._def.type;
|
|
36
|
+
else
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
/** List the parameter names a tool schema accepts (empty array if it takes none). */
|
|
42
|
+
export function getSupportedParams(schema) {
|
|
43
|
+
const shape = getObjectShape(schema);
|
|
44
|
+
return shape ? Object.keys(shape) : [];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Return the names of top-level keys in `args` that the schema does not accept.
|
|
48
|
+
* Returns [] when args is not a plain object or the schema's params can't be
|
|
49
|
+
* determined (so we never warn on something we can't reason about).
|
|
50
|
+
*/
|
|
51
|
+
export function detectUnsupportedParams(args, schema) {
|
|
52
|
+
if (!args || typeof args !== 'object' || Array.isArray(args))
|
|
53
|
+
return [];
|
|
54
|
+
const shape = getObjectShape(schema);
|
|
55
|
+
if (shape === null)
|
|
56
|
+
return [];
|
|
57
|
+
const supported = new Set(Object.keys(shape));
|
|
58
|
+
return Object.keys(args).filter((k) => !supported.has(k));
|
|
59
|
+
}
|
|
60
|
+
/** Build the corrective warning string shown to the model. */
|
|
61
|
+
export function buildUnsupportedParamsWarning(toolName, unsupported, supported) {
|
|
62
|
+
const ignored = unsupported.join(', ');
|
|
63
|
+
const supportedList = supported.length > 0 ? supported.join(', ') : '(none)';
|
|
64
|
+
return `You sent parameters not supported by this tool, which were ignored: ${ignored}. `
|
|
65
|
+
+ `Supported parameters for ${toolName}: ${supportedList}.`;
|
|
66
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
export interface ToolUsageStats {
|
|
2
|
+
filesystemOperations: number;
|
|
3
|
+
terminalOperations: number;
|
|
4
|
+
editOperations: number;
|
|
5
|
+
searchOperations: number;
|
|
6
|
+
configOperations: number;
|
|
7
|
+
processOperations: number;
|
|
8
|
+
totalToolCalls: number;
|
|
9
|
+
successfulCalls: number;
|
|
10
|
+
failedCalls: number;
|
|
11
|
+
toolCounts: Record<string, number>;
|
|
12
|
+
firstUsed: number;
|
|
13
|
+
lastUsed: number;
|
|
14
|
+
totalSessions: number;
|
|
15
|
+
lastFeedbackPrompt: number;
|
|
16
|
+
lastFeedbackPromptDate?: string;
|
|
17
|
+
feedbackAttempts?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface OnboardingState {
|
|
20
|
+
promptsUsed: boolean;
|
|
21
|
+
attemptsShown: number;
|
|
22
|
+
lastShownAt: number;
|
|
23
|
+
}
|
|
24
|
+
export interface UsageSession {
|
|
25
|
+
sessionStart: number;
|
|
26
|
+
lastActivity: number;
|
|
27
|
+
commandsInSession: number;
|
|
28
|
+
promptedThisSession: boolean;
|
|
29
|
+
}
|
|
30
|
+
declare class UsageTracker {
|
|
31
|
+
private currentSession;
|
|
32
|
+
/**
|
|
33
|
+
* Get default usage stats
|
|
34
|
+
*/
|
|
35
|
+
private getDefaultStats;
|
|
36
|
+
/**
|
|
37
|
+
* Get current usage stats from config
|
|
38
|
+
*/
|
|
39
|
+
getStats(): Promise<ToolUsageStats>;
|
|
40
|
+
/**
|
|
41
|
+
* Save usage stats to config.
|
|
42
|
+
* Non-blocking: the tool-call return path must not wait on a disk write. The
|
|
43
|
+
* in-memory stats are already updated by the caller (getStats returns the live
|
|
44
|
+
* config object), so persistence is coalesced in the background. This is what
|
|
45
|
+
* keeps a saturated libuv threadpool from gating tool responses on every call.
|
|
46
|
+
*/
|
|
47
|
+
private saveStats;
|
|
48
|
+
/**
|
|
49
|
+
* Determine which category a tool belongs to
|
|
50
|
+
*/
|
|
51
|
+
private getToolCategory;
|
|
52
|
+
/**
|
|
53
|
+
* Check if we're in a new session
|
|
54
|
+
*/
|
|
55
|
+
private isNewSession;
|
|
56
|
+
/**
|
|
57
|
+
* Update session tracking
|
|
58
|
+
*/
|
|
59
|
+
private updateSession;
|
|
60
|
+
/**
|
|
61
|
+
* Track a successful tool call
|
|
62
|
+
*/
|
|
63
|
+
trackSuccess(toolName: string): Promise<ToolUsageStats>;
|
|
64
|
+
/**
|
|
65
|
+
* Track a failed tool call
|
|
66
|
+
*/
|
|
67
|
+
trackFailure(toolName: string): Promise<ToolUsageStats>;
|
|
68
|
+
/**
|
|
69
|
+
* Check if user should be prompted for feedback based on usage patterns
|
|
70
|
+
*/
|
|
71
|
+
shouldPromptForFeedback(): Promise<boolean>;
|
|
72
|
+
/**
|
|
73
|
+
* Get a random feedback prompt message with strong CTAs and clear actions
|
|
74
|
+
* NEW 2025 Survey - 3 variants for fresh analytics tracking
|
|
75
|
+
*/
|
|
76
|
+
getFeedbackPromptMessage(): Promise<{
|
|
77
|
+
variant: string;
|
|
78
|
+
message: string;
|
|
79
|
+
}>;
|
|
80
|
+
/**
|
|
81
|
+
* Check if user should be prompted for error feedback
|
|
82
|
+
*/
|
|
83
|
+
shouldPromptForErrorFeedback(): Promise<boolean>;
|
|
84
|
+
/**
|
|
85
|
+
* Mark that user was prompted for feedback
|
|
86
|
+
*/
|
|
87
|
+
markFeedbackPrompted(): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Mark that user has given feedback
|
|
90
|
+
*/
|
|
91
|
+
markFeedbackGiven(): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Get usage summary for debugging/admin purposes
|
|
94
|
+
*/
|
|
95
|
+
getUsageSummary(): Promise<string>;
|
|
96
|
+
/**
|
|
97
|
+
* Get onboarding state from config
|
|
98
|
+
*/
|
|
99
|
+
getOnboardingState(): Promise<OnboardingState>;
|
|
100
|
+
/**
|
|
101
|
+
* Save onboarding state to config
|
|
102
|
+
*/
|
|
103
|
+
saveOnboardingState(state: OnboardingState): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Check if user should see onboarding invitation - SIMPLE VERSION
|
|
106
|
+
*/
|
|
107
|
+
shouldShowOnboarding(): Promise<boolean>;
|
|
108
|
+
/**
|
|
109
|
+
* Get onboarding message for new users - DIRECT 5-OPTION LIST (V2)
|
|
110
|
+
*/
|
|
111
|
+
getOnboardingMessage(): Promise<{
|
|
112
|
+
variant: string;
|
|
113
|
+
message: string;
|
|
114
|
+
}>;
|
|
115
|
+
/**
|
|
116
|
+
* Mark that onboarding message was shown - SIMPLE VERSION
|
|
117
|
+
*/
|
|
118
|
+
markOnboardingShown(variant: string): Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* Mark that user used prompts after seeing onboarding invitation - SIMPLE VERSION
|
|
121
|
+
*/
|
|
122
|
+
markOnboardingPromptsUsed(): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Mark that user has used a specific prompt (for analytics)
|
|
125
|
+
*/
|
|
126
|
+
markPromptUsed(promptId: string, category: string): Promise<void>;
|
|
127
|
+
/**
|
|
128
|
+
* Reset onboarding state for testing purposes - SIMPLE VERSION
|
|
129
|
+
*/
|
|
130
|
+
resetOnboardingState(): Promise<void>;
|
|
131
|
+
}
|
|
132
|
+
export declare const usageTracker: UsageTracker;
|
|
133
|
+
export {};
|