@weavetab/mcp 2.5.0-beta.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/AGENTS.md +48 -0
- package/ARCHITECTURE.md +590 -0
- package/LICENSE +631 -0
- package/README.md +909 -0
- package/SECURITY.md +196 -0
- package/TROUBLESHOOTING.md +365 -0
- package/dist/anticaptcha/detector.d.ts +49 -0
- package/dist/anticaptcha/detector.js +465 -0
- package/dist/anticaptcha/integration.d.ts +36 -0
- package/dist/anticaptcha/integration.js +123 -0
- package/dist/anticaptcha/solver.d.ts +27 -0
- package/dist/anticaptcha/solver.js +552 -0
- package/dist/audit/logger.d.ts +13 -0
- package/dist/audit/logger.js +177 -0
- package/dist/audit/telemetry.d.ts +36 -0
- package/dist/audit/telemetry.js +162 -0
- package/dist/cdp/bridge.d.ts +16 -0
- package/dist/cdp/bridge.js +826 -0
- package/dist/cdp/confirm.d.ts +26 -0
- package/dist/cdp/confirm.js +79 -0
- package/dist/cdp/connector.d.ts +37 -0
- package/dist/cdp/connector.js +661 -0
- package/dist/cdp/extension.d.ts +36 -0
- package/dist/cdp/extension.js +118 -0
- package/dist/cdp/finder.d.ts +1 -0
- package/dist/cdp/finder.js +2 -0
- package/dist/cdp/helpers.d.ts +11 -0
- package/dist/cdp/helpers.js +195 -0
- package/dist/cdp/label_cache.d.ts +12 -0
- package/dist/cdp/label_cache.js +51 -0
- package/dist/cdp/lock.d.ts +33 -0
- package/dist/cdp/lock.js +206 -0
- package/dist/cdp/omni_broker.d.ts +17 -0
- package/dist/cdp/omni_broker.js +119 -0
- package/dist/cdp/profiles.d.ts +1 -0
- package/dist/cdp/profiles.js +87 -0
- package/dist/cdp/pruner.d.ts +41 -0
- package/dist/cdp/pruner.js +263 -0
- package/dist/cdp/ref_cache.d.ts +101 -0
- package/dist/cdp/ref_cache.js +234 -0
- package/dist/cdp/retry.d.ts +5 -0
- package/dist/cdp/retry.js +39 -0
- package/dist/cdp/retry_enhanced.d.ts +44 -0
- package/dist/cdp/retry_enhanced.js +102 -0
- package/dist/cdp/security.d.ts +50 -0
- package/dist/cdp/security.js +237 -0
- package/dist/cdp/verify.d.ts +39 -0
- package/dist/cdp/verify.js +143 -0
- package/dist/cdp/walker.d.ts +91 -0
- package/dist/cdp/walker.js +896 -0
- package/dist/cdp/walker.test.d.ts +2 -0
- package/dist/cdp/walker.test.js +172 -0
- package/dist/cdp/websocket_transport.d.ts +15 -0
- package/dist/cdp/websocket_transport.js +118 -0
- package/dist/cli/plugin.d.ts +9 -0
- package/dist/cli/plugin.js +1134 -0
- package/dist/cli/updater.d.ts +1 -0
- package/dist/cli/updater.js +72 -0
- package/dist/config/loader.d.ts +120 -0
- package/dist/config/loader.js +704 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1248 -0
- package/dist/intelligence/hints.d.ts +11 -0
- package/dist/intelligence/hints.js +39 -0
- package/dist/intelligence/intent.d.ts +33 -0
- package/dist/intelligence/intent.js +171 -0
- package/dist/intelligence/sessionMemory.d.ts +66 -0
- package/dist/intelligence/sessionMemory.js +284 -0
- package/dist/intelligence/sessionMemory.test.d.ts +2 -0
- package/dist/intelligence/sessionMemory.test.js +62 -0
- package/dist/intelligence/trail.d.ts +64 -0
- package/dist/intelligence/trail.js +233 -0
- package/dist/intelligence/trail.test.d.ts +2 -0
- package/dist/intelligence/trail.test.js +110 -0
- package/dist/intelligence/vision.d.ts +24 -0
- package/dist/intelligence/vision.js +151 -0
- package/dist/lifecycle/mission_manager.d.ts +16 -0
- package/dist/lifecycle/mission_manager.js +44 -0
- package/dist/overlay/bubble.d.ts +16 -0
- package/dist/overlay/bubble.js +37 -0
- package/dist/overlay/ghost.d.ts +35 -0
- package/dist/overlay/ghost.js +508 -0
- package/dist/overlay/injector.d.ts +22 -0
- package/dist/overlay/injector.js +198 -0
- package/dist/plugin/interface.d.ts +134 -0
- package/dist/plugin/interface.js +1 -0
- package/dist/plugin/loader.d.ts +18 -0
- package/dist/plugin/loader.js +155 -0
- package/dist/plugin/registry.d.ts +50 -0
- package/dist/plugin/registry.js +234 -0
- package/dist/security/blacklist.d.ts +24 -0
- package/dist/security/blacklist.js +80 -0
- package/dist/security/filter.d.ts +2 -0
- package/dist/security/filter.js +20 -0
- package/dist/security/guard.d.ts +8 -0
- package/dist/security/guard.js +35 -0
- package/dist/security/ratelimit.d.ts +11 -0
- package/dist/security/ratelimit.js +57 -0
- package/dist/security/secrets.d.ts +75 -0
- package/dist/security/secrets.js +365 -0
- package/dist/sensors/actionability.d.ts +81 -0
- package/dist/sensors/actionability.js +303 -0
- package/dist/sensors/guard.d.ts +25 -0
- package/dist/sensors/guard.js +168 -0
- package/dist/sensors/mutations.d.ts +23 -0
- package/dist/sensors/mutations.js +93 -0
- package/dist/sensors/network.d.ts +67 -0
- package/dist/sensors/network.js +193 -0
- package/dist/sensors/operation_monitor.d.ts +80 -0
- package/dist/sensors/operation_monitor.js +203 -0
- package/dist/sensors/pacing.d.ts +44 -0
- package/dist/sensors/pacing.js +111 -0
- package/dist/sensors/thoughts.d.ts +2 -0
- package/dist/sensors/thoughts.js +59 -0
- package/dist/sensors/wait.d.ts +12 -0
- package/dist/sensors/wait.js +44 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +1687 -0
- package/dist/state/agent.d.ts +36 -0
- package/dist/state/agent.js +101 -0
- package/dist/state/cache.d.ts +16 -0
- package/dist/state/cache.js +92 -0
- package/dist/state/cache.test.d.ts +2 -0
- package/dist/state/cache.test.js +76 -0
- package/dist/state/delta.d.ts +71 -0
- package/dist/state/delta.js +234 -0
- package/dist/state/delta.test.d.ts +2 -0
- package/dist/state/delta.test.js +279 -0
- package/dist/state/groups.d.ts +15 -0
- package/dist/state/groups.js +80 -0
- package/dist/state/session.d.ts +30 -0
- package/dist/state/session.js +155 -0
- package/dist/tools/accessibility.d.ts +14 -0
- package/dist/tools/accessibility.js +32 -0
- package/dist/tools/ask.d.ts +14 -0
- package/dist/tools/ask.js +49 -0
- package/dist/tools/automation.d.ts +36 -0
- package/dist/tools/automation.js +218 -0
- package/dist/tools/browser_detect.d.ts +13 -0
- package/dist/tools/browser_detect.js +73 -0
- package/dist/tools/burst.d.ts +35 -0
- package/dist/tools/burst.js +344 -0
- package/dist/tools/canvas.d.ts +25 -0
- package/dist/tools/canvas.js +132 -0
- package/dist/tools/captcha.d.ts +38 -0
- package/dist/tools/captcha.js +209 -0
- package/dist/tools/click.d.ts +36 -0
- package/dist/tools/click.js +561 -0
- package/dist/tools/click_and_wait.d.ts +33 -0
- package/dist/tools/click_and_wait.js +149 -0
- package/dist/tools/clipboard.d.ts +29 -0
- package/dist/tools/clipboard.js +279 -0
- package/dist/tools/console.d.ts +21 -0
- package/dist/tools/console.js +107 -0
- package/dist/tools/cookies.d.ts +67 -0
- package/dist/tools/cookies.js +121 -0
- package/dist/tools/design_extract.d.ts +109 -0
- package/dist/tools/design_extract.js +641 -0
- package/dist/tools/dialog.d.ts +24 -0
- package/dist/tools/dialog.js +109 -0
- package/dist/tools/drag.d.ts +15 -0
- package/dist/tools/drag.js +29 -0
- package/dist/tools/emulation.d.ts +33 -0
- package/dist/tools/emulation.js +69 -0
- package/dist/tools/eval.d.ts +20 -0
- package/dist/tools/eval.js +92 -0
- package/dist/tools/fill.d.ts +41 -0
- package/dist/tools/fill.js +667 -0
- package/dist/tools/find.d.ts +30 -0
- package/dist/tools/find.js +60 -0
- package/dist/tools/geo.d.ts +19 -0
- package/dist/tools/geo.js +29 -0
- package/dist/tools/github/analyze.d.ts +4 -0
- package/dist/tools/github/analyze.js +113 -0
- package/dist/tools/github/cache.d.ts +21 -0
- package/dist/tools/github/cache.js +28 -0
- package/dist/tools/github/index.d.ts +4 -0
- package/dist/tools/github/index.js +4 -0
- package/dist/tools/github/issues.d.ts +9 -0
- package/dist/tools/github/issues.js +88 -0
- package/dist/tools/github/pr.d.ts +5 -0
- package/dist/tools/github/pr.js +88 -0
- package/dist/tools/github/rateLimiter.d.ts +4 -0
- package/dist/tools/github/rateLimiter.js +46 -0
- package/dist/tools/github/read.d.ts +34 -0
- package/dist/tools/github/read.js +74 -0
- package/dist/tools/github/repoContext.d.ts +20 -0
- package/dist/tools/github/repoContext.js +129 -0
- package/dist/tools/highlight.d.ts +20 -0
- package/dist/tools/highlight.js +140 -0
- package/dist/tools/hover.d.ts +19 -0
- package/dist/tools/hover.js +57 -0
- package/dist/tools/inspect.d.ts +17 -0
- package/dist/tools/inspect.js +125 -0
- package/dist/tools/integration.test.d.ts +2 -0
- package/dist/tools/integration.test.js +96 -0
- package/dist/tools/key.d.ts +25 -0
- package/dist/tools/key.js +173 -0
- package/dist/tools/macro_compiler.d.ts +7 -0
- package/dist/tools/macro_compiler.js +82 -0
- package/dist/tools/mouse.d.ts +21 -0
- package/dist/tools/mouse.js +62 -0
- package/dist/tools/navigate.d.ts +16 -0
- package/dist/tools/navigate.js +188 -0
- package/dist/tools/network_intercept.d.ts +17 -0
- package/dist/tools/network_intercept.js +151 -0
- package/dist/tools/office/index.d.ts +9 -0
- package/dist/tools/office/index.js +7 -0
- package/dist/tools/office/list.d.ts +15 -0
- package/dist/tools/office/list.js +41 -0
- package/dist/tools/office/parse.d.ts +20 -0
- package/dist/tools/office/parse.js +172 -0
- package/dist/tools/pdf.d.ts +22 -0
- package/dist/tools/pdf.js +42 -0
- package/dist/tools/performance.d.ts +12 -0
- package/dist/tools/performance.js +47 -0
- package/dist/tools/performance_test.d.ts +70 -0
- package/dist/tools/performance_test.js +128 -0
- package/dist/tools/plan.d.ts +39 -0
- package/dist/tools/plan.js +161 -0
- package/dist/tools/pointer.d.ts +35 -0
- package/dist/tools/pointer.js +522 -0
- package/dist/tools/read.d.ts +30 -0
- package/dist/tools/read.js +488 -0
- package/dist/tools/recording.d.ts +45 -0
- package/dist/tools/recording.js +124 -0
- package/dist/tools/response.d.ts +129 -0
- package/dist/tools/response.js +5 -0
- package/dist/tools/scrape.d.ts +24 -0
- package/dist/tools/scrape.js +140 -0
- package/dist/tools/screenshot.d.ts +40 -0
- package/dist/tools/screenshot.js +79 -0
- package/dist/tools/scroll.d.ts +23 -0
- package/dist/tools/scroll.js +196 -0
- package/dist/tools/select.d.ts +13 -0
- package/dist/tools/select.js +217 -0
- package/dist/tools/snapshot.d.ts +27 -0
- package/dist/tools/snapshot.js +99 -0
- package/dist/tools/storage.d.ts +18 -0
- package/dist/tools/storage.js +29 -0
- package/dist/tools/tabs.d.ts +77 -0
- package/dist/tools/tabs.js +342 -0
- package/dist/tools/thought.d.ts +16 -0
- package/dist/tools/thought.js +79 -0
- package/dist/tools/type.d.ts +65 -0
- package/dist/tools/type.js +1061 -0
- package/dist/tools/type_secret.d.ts +23 -0
- package/dist/tools/type_secret.js +229 -0
- package/dist/tools/upload.d.ts +19 -0
- package/dist/tools/upload.js +103 -0
- package/dist/tools/video_compiler.d.ts +24 -0
- package/dist/tools/video_compiler.js +199 -0
- package/dist/tools/viewport.d.ts +44 -0
- package/dist/tools/viewport.js +230 -0
- package/dist/tools/wait.d.ts +19 -0
- package/dist/tools/wait.js +202 -0
- package/dist/tools/wait_for.d.ts +23 -0
- package/dist/tools/wait_for.js +30 -0
- package/dist/tools/windows.d.ts +22 -0
- package/dist/tools/windows.js +57 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +12 -0
- package/docs/DEV_MODE.md +68 -0
- package/docs/DOCUMENTATION.md +532 -0
- package/docs/PLUGINS.md +538 -0
- package/docs/TOOLS.md +104 -0
- package/docs/dev_plugin.md +310 -0
- package/ext-dist/background.js +467 -0
- package/ext-dist/content.js +3089 -0
- package/ext-dist/dashboard.html +583 -0
- package/ext-dist/hud/hud.js +73 -0
- package/ext-dist/hud/index.html +20 -0
- package/ext-dist/manifest.json +77 -0
- package/ext-dist/sidepanel/sidepanel.js +25 -0
- package/ext-dist/sidepanel.html +547 -0
- package/ext-dist/styles.css +218 -0
- package/icon.png +0 -0
- package/mcp.json +796 -0
- package/package.json +113 -0
- package/server.json +23 -0
- package/smithery.yaml +11 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
export class TelemetryCapture {
|
|
2
|
+
session;
|
|
3
|
+
requests = new Map();
|
|
4
|
+
toastMessages = [];
|
|
5
|
+
errors = [];
|
|
6
|
+
navigationOccurred = false;
|
|
7
|
+
newUrl;
|
|
8
|
+
active = false;
|
|
9
|
+
// Store each CDP event listener reference so stop() can remove them.
|
|
10
|
+
// Multiple TelemetryCapture instances per session must not accumulate listeners.
|
|
11
|
+
_onRequestWillBeSent = null;
|
|
12
|
+
_onResponseReceived = null;
|
|
13
|
+
_onLoadingFinished = null;
|
|
14
|
+
_onLoadingFailed = null;
|
|
15
|
+
_onFrameNavigated = null;
|
|
16
|
+
_onChildNodeInserted = null;
|
|
17
|
+
_onConsoleAPICalled = null;
|
|
18
|
+
constructor(session) {
|
|
19
|
+
this.session = session;
|
|
20
|
+
this.setupListeners();
|
|
21
|
+
}
|
|
22
|
+
setupListeners() {
|
|
23
|
+
this._onRequestWillBeSent = (params) => {
|
|
24
|
+
if (!this.active)
|
|
25
|
+
return;
|
|
26
|
+
this.requests.set(params.requestId, {
|
|
27
|
+
url: params.request.url,
|
|
28
|
+
method: params.request.method,
|
|
29
|
+
isApi: params.request.url.includes('/api/') || params.request.headers?.['Accept']?.includes('application/json'),
|
|
30
|
+
timestamp: params.wallTime ? params.wallTime * 1000 : Date.now(),
|
|
31
|
+
_startTimeMonotonic: params.timestamp
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
this.session.Network.requestWillBeSent(this._onRequestWillBeSent);
|
|
35
|
+
this._onResponseReceived = (params) => {
|
|
36
|
+
if (!this.active)
|
|
37
|
+
return;
|
|
38
|
+
const req = this.requests.get(params.requestId);
|
|
39
|
+
if (req) {
|
|
40
|
+
req.status = params.response.status;
|
|
41
|
+
if (params.response.mimeType?.includes('application/json')) {
|
|
42
|
+
req.isApi = true;
|
|
43
|
+
}
|
|
44
|
+
// 1. Capture TLS/SSL Certificate Metadata
|
|
45
|
+
if (params.response.securityDetails) {
|
|
46
|
+
const sec = params.response.securityDetails;
|
|
47
|
+
req.securityDetails = {
|
|
48
|
+
protocol: sec.protocol,
|
|
49
|
+
issuer: sec.issuer,
|
|
50
|
+
subjectName: sec.subjectName,
|
|
51
|
+
validFrom: sec.validFrom,
|
|
52
|
+
validTo: sec.validTo,
|
|
53
|
+
cipher: sec.cipher,
|
|
54
|
+
keyExchange: sec.keyExchange,
|
|
55
|
+
sanList: sec.sanList,
|
|
56
|
+
certificateId: sec.certificateId
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// 2. Capture Network Timing Breakdown (DNS, TCP, SSL, Send, Wait)
|
|
60
|
+
if (params.response.timing) {
|
|
61
|
+
const t = params.response.timing;
|
|
62
|
+
const dnsMs = (t.dnsEnd > 0 && t.dnsStart >= 0) ? Math.max(0, t.dnsEnd - t.dnsStart) : 0;
|
|
63
|
+
const connectMs = (t.connectEnd > 0 && t.connectStart >= 0) ? Math.max(0, t.connectEnd - t.connectStart) : 0;
|
|
64
|
+
const sslMs = (t.sslEnd > 0 && t.sslStart >= 0) ? Math.max(0, t.sslEnd - t.sslStart) : 0;
|
|
65
|
+
const sendMs = (t.sendEnd > 0 && t.sendStart >= 0) ? Math.max(0, t.sendEnd - t.sendStart) : 0;
|
|
66
|
+
const waitMs = (t.receiveHeadersStart > 0 && t.sendEnd >= 0) ? Math.max(0, t.receiveHeadersStart - t.sendEnd) : 0;
|
|
67
|
+
req.timing = {
|
|
68
|
+
dnsMs: Number(dnsMs.toFixed(3)),
|
|
69
|
+
connectMs: Number(connectMs.toFixed(3)),
|
|
70
|
+
sslMs: Number(sslMs.toFixed(3)),
|
|
71
|
+
sendMs: Number(sendMs.toFixed(3)),
|
|
72
|
+
waitMs: Number(waitMs.toFixed(3)),
|
|
73
|
+
};
|
|
74
|
+
if (t.requestTime && t.receiveHeadersStart > 0) {
|
|
75
|
+
req._responseHeadersMonotonic = t.requestTime + (t.receiveHeadersStart / 1000);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
this.session.Network.responseReceived(this._onResponseReceived);
|
|
81
|
+
this._onLoadingFinished = (params) => {
|
|
82
|
+
if (!this.active)
|
|
83
|
+
return;
|
|
84
|
+
const req = this.requests.get(params.requestId);
|
|
85
|
+
if (req) {
|
|
86
|
+
const finishTimeMonotonic = params.timestamp;
|
|
87
|
+
if (req._startTimeMonotonic && finishTimeMonotonic) {
|
|
88
|
+
const totalMs = (finishTimeMonotonic - req._startTimeMonotonic) * 1000;
|
|
89
|
+
if (!req.timing)
|
|
90
|
+
req.timing = {};
|
|
91
|
+
req.timing.totalMs = Number(Math.max(0, totalMs).toFixed(3));
|
|
92
|
+
if (req._responseHeadersMonotonic) {
|
|
93
|
+
const downloadMs = (finishTimeMonotonic - req._responseHeadersMonotonic) * 1000;
|
|
94
|
+
req.timing.downloadMs = Number(Math.max(0, downloadMs).toFixed(3));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
req.timing.downloadMs = 0;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
this.session.Network.loadingFinished(this._onLoadingFinished);
|
|
103
|
+
this._onLoadingFailed = (params) => {
|
|
104
|
+
if (!this.active)
|
|
105
|
+
return;
|
|
106
|
+
const req = this.requests.get(params.requestId);
|
|
107
|
+
if (req) {
|
|
108
|
+
req.failure = {
|
|
109
|
+
errorText: params.errorText || "net::ERR_FAILED",
|
|
110
|
+
canceled: params.canceled,
|
|
111
|
+
blockedReason: params.blockedReason
|
|
112
|
+
};
|
|
113
|
+
this.errors.push(`[Network Error] ${req.url} failed: ${params.errorText || 'net::ERR_FAILED'}`);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
this.session.Network.loadingFailed(this._onLoadingFailed);
|
|
117
|
+
this._onFrameNavigated = (params) => {
|
|
118
|
+
if (!this.active)
|
|
119
|
+
return;
|
|
120
|
+
if (!params.frame.parentId) { // Main frame
|
|
121
|
+
this.navigationOccurred = true;
|
|
122
|
+
this.newUrl = params.frame.url;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
this.session.Page.frameNavigated(this._onFrameNavigated);
|
|
126
|
+
this._onChildNodeInserted = (params) => {
|
|
127
|
+
if (!this.active)
|
|
128
|
+
return;
|
|
129
|
+
const attrs = params.node.attributes || [];
|
|
130
|
+
const isToast = attrs.some((a) => typeof a === 'string' && a.toLowerCase().match(/toast|snackbar|notification|alert/));
|
|
131
|
+
if (isToast) {
|
|
132
|
+
this.toastMessages.push("Toast or banner detected");
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
this.session.DOM.childNodeInserted(this._onChildNodeInserted);
|
|
136
|
+
this._onConsoleAPICalled = (params) => {
|
|
137
|
+
if (!this.active)
|
|
138
|
+
return;
|
|
139
|
+
if (params.type === 'error' || params.type === 'warning') {
|
|
140
|
+
const text = params.args.map((a) => a.value || a.description).join(' ');
|
|
141
|
+
this.errors.push(`[${params.type}] ${text}`);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
this.session.Runtime.consoleAPICalled(this._onConsoleAPICalled);
|
|
145
|
+
}
|
|
146
|
+
async start() {
|
|
147
|
+
this.requests.clear();
|
|
148
|
+
this.toastMessages = [];
|
|
149
|
+
this.errors = [];
|
|
150
|
+
this.navigationOccurred = false;
|
|
151
|
+
this.newUrl = undefined;
|
|
152
|
+
await this.session.Network.enable();
|
|
153
|
+
await this.session.Page.enable();
|
|
154
|
+
await this.session.DOM.enable();
|
|
155
|
+
await this.session.Runtime.enable();
|
|
156
|
+
this.active = true;
|
|
157
|
+
}
|
|
158
|
+
async stop() {
|
|
159
|
+
this.active = false;
|
|
160
|
+
// Remove all listeners to prevent accumulation
|
|
161
|
+
try {
|
|
162
|
+
if (this._onRequestWillBeSent)
|
|
163
|
+
this.session.Network.removeListener('requestWillBeSent', this._onRequestWillBeSent);
|
|
164
|
+
if (this._onResponseReceived)
|
|
165
|
+
this.session.Network.removeListener('responseReceived', this._onResponseReceived);
|
|
166
|
+
if (this._onLoadingFinished)
|
|
167
|
+
this.session.Network.removeListener('loadingFinished', this._onLoadingFinished);
|
|
168
|
+
if (this._onLoadingFailed)
|
|
169
|
+
this.session.Network.removeListener('loadingFailed', this._onLoadingFailed);
|
|
170
|
+
if (this._onFrameNavigated)
|
|
171
|
+
this.session.Page.removeListener('frameNavigated', this._onFrameNavigated);
|
|
172
|
+
if (this._onChildNodeInserted)
|
|
173
|
+
this.session.DOM.removeListener('childNodeInserted', this._onChildNodeInserted);
|
|
174
|
+
if (this._onConsoleAPICalled)
|
|
175
|
+
this.session.Runtime.removeListener('consoleAPICalled', this._onConsoleAPICalled);
|
|
176
|
+
}
|
|
177
|
+
catch { /* ignore */ }
|
|
178
|
+
this._onRequestWillBeSent = this._onResponseReceived = this._onLoadingFinished = this._onLoadingFailed =
|
|
179
|
+
this._onFrameNavigated = this._onChildNodeInserted = this._onConsoleAPICalled = null;
|
|
180
|
+
const requestsClean = Array.from(this.requests.values()).map(r => {
|
|
181
|
+
const { _startTimeMonotonic, _responseHeadersMonotonic, ...publicReq } = r;
|
|
182
|
+
return publicReq;
|
|
183
|
+
});
|
|
184
|
+
return {
|
|
185
|
+
networkRequests: requestsClean,
|
|
186
|
+
domMutations: this.toastMessages.length > 0 ? ["Toasts appeared"] : [], // simplified DOM mutations for now
|
|
187
|
+
navigationOccurred: this.navigationOccurred,
|
|
188
|
+
newUrl: this.newUrl,
|
|
189
|
+
toastMessages: this.toastMessages,
|
|
190
|
+
errors: this.errors
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — sensors/operation_monitor.ts
|
|
3
|
+
* Intelligent operation monitoring for detecting and resolving tool execution lag.
|
|
4
|
+
* Detects when operations hang inside tool execution and provides adaptive bail-out.
|
|
5
|
+
* Weavetab by fy2ne
|
|
6
|
+
*/
|
|
7
|
+
export interface OperationPhase {
|
|
8
|
+
name: string;
|
|
9
|
+
startTime: number;
|
|
10
|
+
endTime?: number;
|
|
11
|
+
duration?: number;
|
|
12
|
+
completed: boolean;
|
|
13
|
+
warning?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface OperationDiagnostics {
|
|
16
|
+
toolName: string;
|
|
17
|
+
totalDuration: number;
|
|
18
|
+
phases: OperationPhase[];
|
|
19
|
+
bottlenecks: string[];
|
|
20
|
+
health: "healthy" | "degraded" | "critical";
|
|
21
|
+
bailOutReason?: string;
|
|
22
|
+
}
|
|
23
|
+
declare class OperationMonitor {
|
|
24
|
+
private activeOperations;
|
|
25
|
+
private readonly PHASE_THRESHOLDS;
|
|
26
|
+
private readonly BAIL_OUT_THRESHOLDS;
|
|
27
|
+
private readonly TOTAL_OPERATION_TIMEOUT;
|
|
28
|
+
/**
|
|
29
|
+
* Start monitoring a new tool operation
|
|
30
|
+
*/
|
|
31
|
+
startOperation(operationId: string, toolName: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Record the start of a specific phase within an operation
|
|
34
|
+
*/
|
|
35
|
+
startPhase(operationId: string, phaseName: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* Record the completion of a specific phase
|
|
38
|
+
*/
|
|
39
|
+
endPhase(operationId: string, phaseName: string, warning?: string): void;
|
|
40
|
+
/**
|
|
41
|
+
* End operation monitoring and return diagnostics
|
|
42
|
+
*/
|
|
43
|
+
endOperation(operationId: string): OperationDiagnostics | null;
|
|
44
|
+
/**
|
|
45
|
+
* Check if an operation should bail out with degraded results
|
|
46
|
+
*/
|
|
47
|
+
shouldBailOut(operationId: string): {
|
|
48
|
+
shouldBail: boolean;
|
|
49
|
+
reason?: string;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Get current operation status for diagnostics
|
|
53
|
+
*/
|
|
54
|
+
getOperationStatus(operationId: string): {
|
|
55
|
+
toolName: string;
|
|
56
|
+
duration: number;
|
|
57
|
+
currentPhase: string;
|
|
58
|
+
health: "healthy" | "degraded" | "critical";
|
|
59
|
+
} | null;
|
|
60
|
+
/**
|
|
61
|
+
* Periodic health check for active operations
|
|
62
|
+
*/
|
|
63
|
+
private checkOperationHealth;
|
|
64
|
+
/**
|
|
65
|
+
* Identify bottlenecks in operation phases
|
|
66
|
+
*/
|
|
67
|
+
private identifyBottlenecks;
|
|
68
|
+
/**
|
|
69
|
+
* Determine overall operation health
|
|
70
|
+
*/
|
|
71
|
+
private determineHealth;
|
|
72
|
+
/**
|
|
73
|
+
* Clear every active diagnostics timer. Registered on process exit events so
|
|
74
|
+
* that a tool crashing before endOperation() can't leave an interval firing
|
|
75
|
+
* every second and pinning the Node.js event loop.
|
|
76
|
+
*/
|
|
77
|
+
cleanupAll(): void;
|
|
78
|
+
}
|
|
79
|
+
export declare const operationMonitor: OperationMonitor;
|
|
80
|
+
export {};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { logDebug } from "../audit/logger.js";
|
|
2
|
+
class OperationMonitor {
|
|
3
|
+
activeOperations = new Map();
|
|
4
|
+
PHASE_THRESHOLDS = {
|
|
5
|
+
// Expected maximum duration for each phase (in ms)
|
|
6
|
+
setup: 500, // Security checks, session setup
|
|
7
|
+
element_resolution: 1000, // Finding elements in DOM
|
|
8
|
+
actionability_check: 800, // Checking if element is clickable
|
|
9
|
+
ghost_move: 500, // Ghost cursor movement
|
|
10
|
+
action_dispatch: 300, // Actual click/type dispatch
|
|
11
|
+
stuck_check: 1000, // Post-action stuck detection
|
|
12
|
+
network_wait: 3000, // Network idle waiting
|
|
13
|
+
dom_stability: 1500, // DOM stability checks
|
|
14
|
+
};
|
|
15
|
+
BAIL_OUT_THRESHOLDS = {
|
|
16
|
+
// When to bail out with degraded results (in ms)
|
|
17
|
+
element_resolution: 2500,
|
|
18
|
+
actionability_check: 2000,
|
|
19
|
+
network_wait: 6000,
|
|
20
|
+
dom_stability: 4000,
|
|
21
|
+
};
|
|
22
|
+
TOTAL_OPERATION_TIMEOUT = 15000; // 15s max for any operation
|
|
23
|
+
/**
|
|
24
|
+
* Start monitoring a new tool operation
|
|
25
|
+
*/
|
|
26
|
+
startOperation(operationId, toolName) {
|
|
27
|
+
this.activeOperations.set(operationId, {
|
|
28
|
+
toolName,
|
|
29
|
+
startTime: Date.now(),
|
|
30
|
+
phases: [],
|
|
31
|
+
});
|
|
32
|
+
// Start periodic health check
|
|
33
|
+
const diagnosticsTimer = setInterval(() => {
|
|
34
|
+
this.checkOperationHealth(operationId);
|
|
35
|
+
}, 1000); // Check every 1 second (reduced from 2s for faster detection)
|
|
36
|
+
this.activeOperations.get(operationId).diagnosticsTimer = diagnosticsTimer;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Record the start of a specific phase within an operation
|
|
40
|
+
*/
|
|
41
|
+
startPhase(operationId, phaseName) {
|
|
42
|
+
const op = this.activeOperations.get(operationId);
|
|
43
|
+
if (!op)
|
|
44
|
+
return;
|
|
45
|
+
op.phases.push({
|
|
46
|
+
name: phaseName,
|
|
47
|
+
startTime: Date.now(),
|
|
48
|
+
completed: false,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Record the completion of a specific phase
|
|
53
|
+
*/
|
|
54
|
+
endPhase(operationId, phaseName, warning) {
|
|
55
|
+
const op = this.activeOperations.get(operationId);
|
|
56
|
+
if (!op)
|
|
57
|
+
return;
|
|
58
|
+
const phase = op.phases.find(p => p.name === phaseName && !p.completed);
|
|
59
|
+
if (phase) {
|
|
60
|
+
phase.endTime = Date.now();
|
|
61
|
+
phase.duration = phase.endTime - phase.startTime;
|
|
62
|
+
phase.completed = true;
|
|
63
|
+
phase.warning = warning;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* End operation monitoring and return diagnostics
|
|
68
|
+
*/
|
|
69
|
+
endOperation(operationId) {
|
|
70
|
+
const op = this.activeOperations.get(operationId);
|
|
71
|
+
if (!op)
|
|
72
|
+
return null;
|
|
73
|
+
// Clear diagnostics timer
|
|
74
|
+
if (op.diagnosticsTimer) {
|
|
75
|
+
clearInterval(op.diagnosticsTimer);
|
|
76
|
+
}
|
|
77
|
+
const totalDuration = Date.now() - op.startTime;
|
|
78
|
+
const bottlenecks = this.identifyBottlenecks(op.phases);
|
|
79
|
+
const health = this.determineHealth(totalDuration, bottlenecks);
|
|
80
|
+
const diagnostics = {
|
|
81
|
+
toolName: op.toolName,
|
|
82
|
+
totalDuration,
|
|
83
|
+
phases: op.phases,
|
|
84
|
+
bottlenecks,
|
|
85
|
+
health,
|
|
86
|
+
};
|
|
87
|
+
this.activeOperations.delete(operationId);
|
|
88
|
+
return diagnostics;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check if an operation should bail out with degraded results
|
|
92
|
+
*/
|
|
93
|
+
shouldBailOut(operationId) {
|
|
94
|
+
const op = this.activeOperations.get(operationId);
|
|
95
|
+
if (!op)
|
|
96
|
+
return { shouldBail: false };
|
|
97
|
+
const totalDuration = Date.now() - op.startTime;
|
|
98
|
+
// Check total operation timeout
|
|
99
|
+
if (totalDuration > this.TOTAL_OPERATION_TIMEOUT) {
|
|
100
|
+
return { shouldBail: true, reason: `Total operation timeout (${totalDuration}ms > ${this.TOTAL_OPERATION_TIMEOUT}ms)` };
|
|
101
|
+
}
|
|
102
|
+
// Check specific phase timeouts
|
|
103
|
+
const currentPhase = op.phases.find(p => !p.completed);
|
|
104
|
+
if (currentPhase) {
|
|
105
|
+
const phaseDuration = Date.now() - currentPhase.startTime;
|
|
106
|
+
const bailOutThreshold = this.BAIL_OUT_THRESHOLDS[currentPhase.name];
|
|
107
|
+
if (bailOutThreshold && phaseDuration > bailOutThreshold) {
|
|
108
|
+
return {
|
|
109
|
+
shouldBail: true,
|
|
110
|
+
reason: `Phase '${currentPhase.name}' timeout (${phaseDuration}ms > ${bailOutThreshold}ms)`
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return { shouldBail: false };
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get current operation status for diagnostics
|
|
118
|
+
*/
|
|
119
|
+
getOperationStatus(operationId) {
|
|
120
|
+
const op = this.activeOperations.get(operationId);
|
|
121
|
+
if (!op)
|
|
122
|
+
return null;
|
|
123
|
+
const duration = Date.now() - op.startTime;
|
|
124
|
+
const currentPhase = op.phases.find(p => !p.completed)?.name || "idle";
|
|
125
|
+
const health = this.determineHealth(duration, []);
|
|
126
|
+
return {
|
|
127
|
+
toolName: op.toolName,
|
|
128
|
+
duration,
|
|
129
|
+
currentPhase,
|
|
130
|
+
health,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Periodic health check for active operations
|
|
135
|
+
*/
|
|
136
|
+
checkOperationHealth(operationId) {
|
|
137
|
+
const op = this.activeOperations.get(operationId);
|
|
138
|
+
if (!op)
|
|
139
|
+
return;
|
|
140
|
+
const duration = Date.now() - op.startTime;
|
|
141
|
+
const currentPhase = op.phases.find(p => !p.completed);
|
|
142
|
+
if (currentPhase) {
|
|
143
|
+
const phaseDuration = Date.now() - currentPhase.startTime;
|
|
144
|
+
const threshold = this.PHASE_THRESHOLDS[currentPhase.name];
|
|
145
|
+
if (threshold && phaseDuration > threshold) {
|
|
146
|
+
// Phase is taking longer than expected
|
|
147
|
+
logDebug(`[Weavetab Monitor] Phase '${currentPhase.name}' taking longer than expected: ${phaseDuration}ms (threshold: ${threshold}ms)`);
|
|
148
|
+
// Add warning to phase
|
|
149
|
+
currentPhase.warning = `Exceeded threshold by ${phaseDuration - threshold}ms`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (duration > this.TOTAL_OPERATION_TIMEOUT * 0.7) {
|
|
153
|
+
// Operation is approaching timeout
|
|
154
|
+
logDebug(`[Weavetab Monitor] Operation '${op.toolName}' approaching timeout: ${duration}ms / ${this.TOTAL_OPERATION_TIMEOUT}ms`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Identify bottlenecks in operation phases
|
|
159
|
+
*/
|
|
160
|
+
identifyBottlenecks(phases) {
|
|
161
|
+
const bottlenecks = [];
|
|
162
|
+
for (const phase of phases) {
|
|
163
|
+
if (!phase.completed || !phase.duration)
|
|
164
|
+
continue;
|
|
165
|
+
const threshold = this.PHASE_THRESHOLDS[phase.name];
|
|
166
|
+
if (threshold && phase.duration > threshold) {
|
|
167
|
+
bottlenecks.push(`${phase.name}: ${phase.duration}ms (threshold: ${threshold}ms)`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return bottlenecks;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Determine overall operation health
|
|
174
|
+
*/
|
|
175
|
+
determineHealth(duration, bottlenecks) {
|
|
176
|
+
if (bottlenecks.length > 2 || duration > this.TOTAL_OPERATION_TIMEOUT * 0.8) {
|
|
177
|
+
return "critical";
|
|
178
|
+
}
|
|
179
|
+
if (bottlenecks.length > 0 || duration > this.TOTAL_OPERATION_TIMEOUT * 0.5) {
|
|
180
|
+
return "degraded";
|
|
181
|
+
}
|
|
182
|
+
return "healthy";
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Clear every active diagnostics timer. Registered on process exit events so
|
|
186
|
+
* that a tool crashing before endOperation() can't leave an interval firing
|
|
187
|
+
* every second and pinning the Node.js event loop.
|
|
188
|
+
*/
|
|
189
|
+
cleanupAll() {
|
|
190
|
+
for (const [id, op] of this.activeOperations.entries()) {
|
|
191
|
+
if (op.diagnosticsTimer) {
|
|
192
|
+
clearInterval(op.diagnosticsTimer);
|
|
193
|
+
}
|
|
194
|
+
this.activeOperations.delete(id);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
export const operationMonitor = new OperationMonitor();
|
|
199
|
+
// Register shutdown hooks so timers are always cleared, even on crashes
|
|
200
|
+
process.on('exit', () => operationMonitor.cleanupAll());
|
|
201
|
+
process.on('SIGINT', () => { operationMonitor.cleanupAll(); });
|
|
202
|
+
process.on('SIGTERM', () => { operationMonitor.cleanupAll(); });
|
|
203
|
+
process.on('uncaughtException', () => { operationMonitor.cleanupAll(); });
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — src/sensors/pacing.ts
|
|
3
|
+
* Human Pacing System — pass-through wrapper that records timing telemetry.
|
|
4
|
+
* All human-like delays live in ghost.ts (ghostMove, ghostType).
|
|
5
|
+
* This file does NOT add its own delays — eliminates the "double pacing" bug.
|
|
6
|
+
* Weavetab by fy2ne
|
|
7
|
+
*/
|
|
8
|
+
import CDP from "chrome-remote-interface";
|
|
9
|
+
export declare const HumanPace: {
|
|
10
|
+
afterRead: () => Promise<unknown>;
|
|
11
|
+
betweenClicks: () => Promise<unknown>;
|
|
12
|
+
afterNavigate: () => Promise<unknown>;
|
|
13
|
+
thinkingPause: () => Promise<unknown>;
|
|
14
|
+
fieldToField: () => Promise<unknown>;
|
|
15
|
+
beforeSubmit: () => Promise<unknown>;
|
|
16
|
+
};
|
|
17
|
+
export interface ToolCallMetrics {
|
|
18
|
+
tool: string;
|
|
19
|
+
inputTokens: number;
|
|
20
|
+
outputTokens: number;
|
|
21
|
+
totalTokens: number;
|
|
22
|
+
latencyMs: number;
|
|
23
|
+
timestamp: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Returns a session-level usage summary.
|
|
27
|
+
* Exposed to browser_eval via window.__wt_get_usage().
|
|
28
|
+
*/
|
|
29
|
+
export declare function getUsageStats(): {
|
|
30
|
+
totalCalls: number;
|
|
31
|
+
totalTokens: number;
|
|
32
|
+
byTool: {
|
|
33
|
+
tool: string;
|
|
34
|
+
calls: number;
|
|
35
|
+
tokens: number;
|
|
36
|
+
avgLatencyMs: number;
|
|
37
|
+
}[];
|
|
38
|
+
topSlowest: string[];
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Wraps a tool action — records timing + updates HUD state.
|
|
42
|
+
* No artificial delays added here; ghost.ts handles human-like pacing.
|
|
43
|
+
*/
|
|
44
|
+
export declare function agentAction(session: CDP.Client, type: 'clicking' | 'typing' | 'reading' | 'thinking' | 'navigating', fn: () => Promise<any>, toolName?: string): Promise<any>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { sendToExtension } from "../cdp/helpers.js";
|
|
2
|
+
import { agentState } from "../state/agent.js";
|
|
3
|
+
import { getRandomThought } from "./thoughts.js";
|
|
4
|
+
export const HumanPace = {
|
|
5
|
+
afterRead: () => sleep(randomBetween(800, 2500)),
|
|
6
|
+
betweenClicks: () => sleep(randomBetween(300, 800)),
|
|
7
|
+
afterNavigate: () => sleep(randomBetween(1500, 3500)),
|
|
8
|
+
thinkingPause: () => sleep(randomBetween(2000, 5000)),
|
|
9
|
+
fieldToField: () => sleep(randomBetween(400, 900)),
|
|
10
|
+
beforeSubmit: () => sleep(randomBetween(500, 1500)),
|
|
11
|
+
};
|
|
12
|
+
function sleep(ms) {
|
|
13
|
+
return new Promise(r => setTimeout(r, ms));
|
|
14
|
+
}
|
|
15
|
+
function randomBetween(min, max) {
|
|
16
|
+
return Math.random() * (max - min) + min;
|
|
17
|
+
}
|
|
18
|
+
/** Rolling log of the last 100 tool calls for devMode HUD and usage() queries. */
|
|
19
|
+
const toolCallLog = [];
|
|
20
|
+
const MAX_LOG_SIZE = 100;
|
|
21
|
+
function estimateTokens(obj) {
|
|
22
|
+
try {
|
|
23
|
+
return Math.ceil(JSON.stringify(obj).length / 4);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function recordMetrics(metrics) {
|
|
30
|
+
toolCallLog.push(metrics);
|
|
31
|
+
if (toolCallLog.length > MAX_LOG_SIZE)
|
|
32
|
+
toolCallLog.shift();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns a session-level usage summary.
|
|
36
|
+
* Exposed to browser_eval via window.__wt_get_usage().
|
|
37
|
+
*/
|
|
38
|
+
export function getUsageStats() {
|
|
39
|
+
const byTool = new Map();
|
|
40
|
+
for (const entry of toolCallLog) {
|
|
41
|
+
const existing = byTool.get(entry.tool) ?? { calls: 0, tokens: 0, totalLatencyMs: 0 };
|
|
42
|
+
existing.calls++;
|
|
43
|
+
existing.tokens += entry.totalTokens;
|
|
44
|
+
existing.totalLatencyMs += entry.latencyMs;
|
|
45
|
+
byTool.set(entry.tool, existing);
|
|
46
|
+
}
|
|
47
|
+
const totalCalls = toolCallLog.length;
|
|
48
|
+
const totalTokens = toolCallLog.reduce((s, e) => s + e.totalTokens, 0);
|
|
49
|
+
const toolEntries = [...byTool.entries()].map(([tool, data]) => ({
|
|
50
|
+
tool,
|
|
51
|
+
calls: data.calls,
|
|
52
|
+
tokens: data.tokens,
|
|
53
|
+
avgLatencyMs: Math.round(data.totalLatencyMs / data.calls),
|
|
54
|
+
}));
|
|
55
|
+
const topSlowest = [...toolCallLog]
|
|
56
|
+
.sort((a, b) => b.latencyMs - a.latencyMs)
|
|
57
|
+
.slice(0, 5)
|
|
58
|
+
.map(e => `${e.tool} (${e.latencyMs}ms)`);
|
|
59
|
+
return { totalCalls, totalTokens, byTool: toolEntries, topSlowest };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Wraps a tool action — records timing + updates HUD state.
|
|
63
|
+
* No artificial delays added here; ghost.ts handles human-like pacing.
|
|
64
|
+
*/
|
|
65
|
+
export async function agentAction(session, type, fn, toolName) {
|
|
66
|
+
// Report tool activity to agent state manager
|
|
67
|
+
agentState.reportToolCall();
|
|
68
|
+
// Update state indicator with a varied thought (no cursor position — ghost.ts handles real cursor)
|
|
69
|
+
const thought = getRandomThought(type);
|
|
70
|
+
sendToExtension(session, {
|
|
71
|
+
type: 'Weavetab_STATE',
|
|
72
|
+
state: type,
|
|
73
|
+
...(thought ? { hud: { action: thought } } : {}),
|
|
74
|
+
}).catch(() => { });
|
|
75
|
+
const start = Date.now();
|
|
76
|
+
const result = await fn();
|
|
77
|
+
const elapsed = Date.now() - start;
|
|
78
|
+
// Record execution timing for telemetry
|
|
79
|
+
if (result && typeof result === 'object') {
|
|
80
|
+
result.execution_ms = elapsed;
|
|
81
|
+
}
|
|
82
|
+
// Token telemetry — approximate via JSON length / 4
|
|
83
|
+
if (toolName) {
|
|
84
|
+
const inputTokens = estimateTokens(result); // args not captured here, output used as proxy
|
|
85
|
+
const outputTokens = estimateTokens(result);
|
|
86
|
+
const metrics = {
|
|
87
|
+
tool: toolName,
|
|
88
|
+
inputTokens,
|
|
89
|
+
outputTokens,
|
|
90
|
+
totalTokens: inputTokens + outputTokens,
|
|
91
|
+
latencyMs: elapsed,
|
|
92
|
+
timestamp: start,
|
|
93
|
+
};
|
|
94
|
+
recordMetrics(metrics);
|
|
95
|
+
// DevMode HUD console output
|
|
96
|
+
const liveConfig = global.__wt_live_config;
|
|
97
|
+
const devMode = liveConfig?.devMode;
|
|
98
|
+
const devEnabled = devMode === true;
|
|
99
|
+
if (devEnabled) {
|
|
100
|
+
const stats = getUsageStats();
|
|
101
|
+
process.stderr.write(`\n┌─ Tool Call ──────────────────────────────────────────┐\n` +
|
|
102
|
+
`│ ${toolName.padEnd(50)} │\n` +
|
|
103
|
+
`│ Input: ${String(inputTokens).padStart(4)} tokens │ Latency: ${String(elapsed).padStart(6)}ms │\n` +
|
|
104
|
+
`│ Output: ${String(outputTokens).padStart(4)} tokens │ Total: ${String(metrics.totalTokens).padStart(4)} tokens │\n` +
|
|
105
|
+
`│ ──────────────────────────────────────────────────── │\n` +
|
|
106
|
+
`│ Session total: ${stats.totalTokens} tokens (${stats.totalCalls} calls)`.padEnd(53) + `│\n` +
|
|
107
|
+
`└──────────────────────────────────────────────────────┘\n`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|