UNPKG

posthog-js

Version:

Posthog-js allows you to automatically capture usage and send events to PostHog.

445 lines • 18.6 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WebMCP = void 0; var core_1 = require("@posthog/core"); var globals_1 = require("../utils/globals"); var patch_1 = require("./replay/rrweb-plugins/patch"); var instrumentedModelContexts = new WeakMap(); var CONTEXT_PARAMETER_DESCRIPTION = "Explain why this tool is called and how it supports the user's goal. Describe the abstract purpose only. Do not include personal or identifying information. Generalize specific entities as roles such as \"a customer\"."; var MODEL_PARAMETER_DESCRIPTION = 'The exact model identifier you are running as, taken from your system prompt or environment. Pass "unknown" if you do not know it. Never guess.'; var INJECTABLE_SCHEMA_KEYS = new Set([ '$comment', '$defs', '$id', '$schema', 'additionalProperties', 'default', 'definitions', 'deprecated', 'description', 'examples', 'minProperties', 'properties', 'readOnly', 'required', 'title', 'type', 'writeOnly', ]); var MAX_INTENT_LENGTH = 2048; var MAX_MODEL_LENGTH = 256; var MAX_ERROR_MESSAGE_LENGTH = 2048; var MAX_ERROR_TYPE_LENGTH = 256; var INTENT_SANITIZATION_OPTIONS = { maxStringLength: MAX_INTENT_LENGTH, truncationSuffix: '...' }; var ERROR_SANITIZATION_OPTIONS = { maxStringLength: MAX_ERROR_MESSAGE_LENGTH, truncationSuffix: '...' }; function getMetadataOptions(config) { if (!config) { return undefined; } if (config === true) { return { intent: true, model: true }; } return { intent: config.intent !== false, model: config.model !== false }; } function getRequestedMetadata(instrumentation) { var e_1, _a; var requested = { intent: false, model: false }; try { for (var _b = __values(instrumentation.instances), _c = _b.next(); !_c.done; _c = _b.next()) { var instance = _c.value; var options = getMetadataOptions(instance.config.capture_webmcp); requested.intent || (requested.intent = (options === null || options === void 0 ? void 0 : options.intent) === true); requested.model || (requested.model = (options === null || options === void 0 ? void 0 : options.model) === true); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } return requested; } function hasOwn(value, key) { return Object.prototype.hasOwnProperty.call(value, key); } function copyTool(tool, inputSchema) { var copiedTool = Object.create(tool); if (inputSchema) { Object.defineProperty(copiedTool, 'inputSchema', { configurable: true, enumerable: true, writable: true, value: inputSchema, }); } return copiedTool; } function canInjectInto(schema) { if ((0, core_1.isUndefined)(schema)) { return true; } return ((0, core_1.isObject)(schema) && Object.keys(schema).every(function (key) { return INJECTABLE_SCHEMA_KEYS.has(key) || key.startsWith('x-'); }) && schema.additionalProperties !== true && !(0, core_1.isObject)(schema.additionalProperties) && (!schema.type || schema.type === 'object') && (!schema.properties || (0, core_1.isObject)(schema.properties)) && (!schema.required || (0, core_1.isArray)(schema.required))); } function injectMetadataParameters(tool, requested) { var injected = { intent: false, model: false }; var schema = tool.inputSchema; if (!canInjectInto(schema)) { return { tool: copyTool(tool), injected: injected }; } var properties = (schema === null || schema === void 0 ? void 0 : schema.properties) || {}; var declared = function (name) { return hasOwn(properties, name) || ((0, core_1.isArray)(schema === null || schema === void 0 ? void 0 : schema.required) && schema.required.includes(name)); }; injected.intent = requested.intent && !declared('context'); injected.model = requested.model && !declared('llm_model'); if (!injected.intent && !injected.model) { return { tool: copyTool(tool), injected: injected }; } var nextProperties = __assign({}, properties); var required = (0, core_1.isArray)(schema === null || schema === void 0 ? void 0 : schema.required) ? __spreadArray([], __read(schema.required), false) : []; if (injected.intent) { nextProperties.context = { type: 'string', description: CONTEXT_PARAMETER_DESCRIPTION }; if (!required.includes('context')) { required.push('context'); } } if (injected.model) { nextProperties.llm_model = { type: 'string', description: MODEL_PARAMETER_DESCRIPTION }; if (!required.includes('llm_model')) { required.push('llm_model'); } } return { tool: copyTool(tool, __assign(__assign({}, schema), { type: (schema === null || schema === void 0 ? void 0 : schema.type) || 'object', properties: nextProperties, required: required })), injected: injected, }; } function normalizeMetadata(value, maxLength, redact) { if (redact === void 0) { redact = false; } if (!(0, core_1.isString)(value)) { return undefined; } var normalized = value.trim(); if (!normalized) { return undefined; } var valueToCapture = redact ? (0, core_1.sanitizeFreeText)(normalized, INTENT_SANITIZATION_OPTIONS) : normalized; return valueToCapture.slice(0, maxLength); } function getCallMetadata(input, injected) { if (!(0, core_1.isObject)(input)) { return {}; } try { var intent = injected.intent ? normalizeMetadata(input.context, MAX_INTENT_LENGTH, true) : undefined; var model = injected.model ? normalizeMetadata(input.llm_model, MAX_MODEL_LENGTH) : undefined; return __assign(__assign({}, (intent && intent !== '{}' ? { intent: intent } : {})), (model && model.toLowerCase() !== 'unknown' ? { model: model } : {})); } catch (_a) { return {}; } } function stripInjectedMetadata(args, injected) { var input = args[0]; if (!(0, core_1.isObject)(input)) { return args; } try { var removeIntent = injected.intent && hasOwn(input, 'context'); var removeModel = injected.model && hasOwn(input, 'llm_model'); if (!removeIntent && !removeModel) { return args; } var toolInput = __assign({}, input); if (removeIntent) { delete toolInput.context; } if (removeModel) { delete toolInput.llm_model; } return __spreadArray([toolInput], __read(args.slice(1)), false); } catch (_a) { return args; } } function isErrorResult(value) { try { return (0, core_1.isObject)(value) && value.isError === true; } catch (_a) { return false; } } function getErrorResultMessage(value) { try { if (!(0, core_1.isObject)(value) || !(0, core_1.isArray)(value.content)) { return undefined; } var parts = value.content .filter(function (part) { return (0, core_1.isObject)(part) && part.type === 'text' && (0, core_1.isString)(part.text); }) .map(function (part) { return part.text; }); return parts.join(' ').trim() || undefined; } catch (_a) { return undefined; } } function sanitizeErrorMessage(message) { return (0, core_1.sanitizeFreeText)(message || 'Unknown error', ERROR_SANITIZATION_OPTIONS).slice(0, MAX_ERROR_MESSAGE_LENGTH); } function getErrorMessage(error, errorResult) { try { var message = errorResult ? getErrorResultMessage(error) : (0, core_1.isObject)(error) && (0, core_1.isString)(error.message) ? error.message : (0, core_1.isString)(error) ? error : String(error); return sanitizeErrorMessage(message); } catch (_a) { return 'Unknown error'; } } function getExceptionList(exceptions, failureInput, message) { if (failureInput.errorResult) { return [ { type: 'Error', value: message, mechanism: { type: 'generic', handled: true, synthetic: true, exception_id: 0 }, }, ]; } return exceptions.buildProperties(failureInput.error, { handled: true }).$exception_list.map(function (exception) { return (__assign(__assign({}, exception), { value: sanitizeErrorMessage(exception.value) })); }); } function getErrorType(error, errorResult) { if (errorResult) { return 'Error'; } try { if (!(0, core_1.isObject)(error)) { return 'Error'; } var type = (0, core_1.isString)(error.name) ? error.name.trim() : ''; var constructor = error.constructor; if ((!type || type === 'Error') && (0, core_1.isFunction)(constructor) && constructor.name !== 'Error') { type = constructor.name; } return (type || 'Error').slice(0, MAX_ERROR_TYPE_LENGTH); } catch (_a) { return 'Error'; } } function getFailure(error, errorResult) { var message = getErrorMessage(error, errorResult); return { message: message, type: getErrorType(error, errorResult), }; } var WebMCP = /** @class */ (function () { function WebMCP(_instance) { this._instance = _instance; this._isPatched = false; } WebMCP.prototype.initialize = function () { this.startIfEnabled(); }; WebMCP.prototype.startIfEnabled = function () { if (!this._instance.config.capture_webmcp || this._isPatched) { return; } var modelContext = globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.modelContext; if (!modelContext || !(0, core_1.isFunction)(modelContext.registerTool)) { return; } var existingInstrumentation = instrumentedModelContexts.get(modelContext); if (existingInstrumentation) { existingInstrumentation.instances.add(this._instance); this._isPatched = true; return; } var instrumentation = { instances: new Set([this._instance]) }; var unpatchedRegisterTool = modelContext.registerTool; var webMCP = this; (0, patch_1.patch)(modelContext, 'registerTool', function (originalRegisterTool) { var registerTool = originalRegisterTool; return function (tool) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } var registeredTool = tool; try { registeredTool = webMCP._wrapTool(tool, instrumentation); } catch (_a) { return registerTool.call.apply(registerTool, __spreadArray([this, tool], __read(args), false)); } return registerTool.call.apply(registerTool, __spreadArray([this, registeredTool], __read(args), false)); }; }); if (modelContext.registerTool === unpatchedRegisterTool) { return; } instrumentedModelContexts.set(modelContext, instrumentation); this._isPatched = true; }; WebMCP.prototype._wrapTool = function (tool, instrumentation) { if (!tool || !(0, core_1.isFunction)(tool.execute)) { return tool; } var execute = tool.execute; var webMCP = this; var _a = injectMetadataParameters(tool, getRequestedMetadata(instrumentation)), wrappedTool = _a.tool, injected = _a.injected; Object.defineProperty(wrappedTool, 'execute', { configurable: true, enumerable: true, writable: true, value: function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var startedAt = new Date(); var metadata = getCallMetadata(args[0], injected); var toolArgs = stripInjectedMetadata(args, injected); var result; try { result = execute.apply(this === wrappedTool ? tool : this, toolArgs); } catch (error) { webMCP._captureToolCall(instrumentation, wrappedTool, startedAt, { error: error, errorResult: false }, metadata); throw error; } if ((0, core_1.isPromise)(result)) { return Promise.resolve(result).then(function (value) { var failure = isErrorResult(value) ? { error: value, errorResult: true } : undefined; webMCP._captureToolCall(instrumentation, wrappedTool, startedAt, failure, metadata); return value; }, function (error) { webMCP._captureToolCall(instrumentation, wrappedTool, startedAt, { error: error, errorResult: false }, metadata); throw error; }); } var failure = isErrorResult(result) ? { error: result, errorResult: true } : undefined; webMCP._captureToolCall(instrumentation, wrappedTool, startedAt, failure, metadata); return result; }, }); return wrappedTool; }; WebMCP.prototype._captureToolCall = function (instrumentation, tool, timestamp, failureInput, metadata) { var e_2, _a; var _b; var duration = Date.now() - timestamp.getTime(); var failure; try { for (var _c = __values(instrumentation.instances), _d = _c.next(); !_d.done; _d = _c.next()) { var instance = _d.value; var options = getMetadataOptions(instance.config.capture_webmcp); if (!options) { continue; } if (failureInput && !failure) { failure = getFailure(failureInput.error, failureInput.errorResult); } try { instance.capture('$mcp_tool_call', __assign(__assign(__assign({ $mcp_source: 'posthog_mcp_analytics', $mcp_interface: 'webmcp', $mcp_tool_name: tool.name, $mcp_resource_name: tool.name, $mcp_tool_description: tool.description, $mcp_server_name: globals_1.location === null || globals_1.location === void 0 ? void 0 : globals_1.location.hostname, $mcp_duration_ms: duration, $mcp_is_error: !!failureInput }, (failure ? { $mcp_error_type: failure.type, $mcp_error_message: failure.message } : {})), (options.intent && metadata.intent ? { $mcp_intent: metadata.intent, $mcp_intent_source: 'context_parameter' } : {})), (options.model && metadata.model ? { $mcp_llm_model: metadata.model, $mcp_llm_model_source: 'self_reported' } : {})), { timestamp: timestamp }); } catch (_e) { continue; } if (failureInput && failure && instance.exceptions && ((_b = instance.exceptionObserver) === null || _b === void 0 ? void 0 : _b.isEnabled)) { try { var properties = { $exception_list: getExceptionList(instance.exceptions, failureInput, failure.message), $exception_level: 'error', $exception_source: 'mcp.tool_call', $mcp_interface: 'webmcp', $mcp_tool_name: tool.name, $mcp_resource_name: tool.name, $mcp_tool_description: tool.description, $mcp_server_name: globals_1.location === null || globals_1.location === void 0 ? void 0 : globals_1.location.hostname, }; instance.exceptionObserver.captureException(properties); } catch (_f) { } } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_d && !_d.done && (_a = _c.return)) _a.call(_c); } finally { if (e_2) throw e_2.error; } } }; return WebMCP; }()); exports.WebMCP = WebMCP; //# sourceMappingURL=webmcp.js.map