openlit
Version:
OpenTelemetry-native Auto instrumentation library for monitoring LLM Applications, facilitating the integration of observability into your GenAI-driven projects
689 lines • 32.2 kB
JavaScript
;
/**
* Cursor SDK wrapper -- OTel GenAI semantic convention compliant.
*
* Wraps Agent.create(), Agent.resume(), and agent.send()
* to produce `create_agent`, `invoke_agent`, and `execute_tool` spans.
*
* Agent.prompt() is NOT wrapped separately -- it internally calls
* create() + send(), so the patched versions handle it automatically
* without producing duplicate spans.
*
* Token usage is captured via onDelta injection (TurnEndedUpdate).
* Tool call spans are created from SDKMessage stream events.
* The `system` stream event provides resolved model and tool definitions.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.patchAgentCreate = patchAgentCreate;
exports.patchAgentResume = patchAgentResume;
const api_1 = require("@opentelemetry/api");
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
const semantic_convention_1 = __importDefault(require("../../semantic-convention"));
const config_1 = __importDefault(require("../../config"));
const helpers_1 = __importStar(require("../../helpers"));
const constant_1 = require("../../constant");
const metrics_1 = __importDefault(require("../../otel/metrics"));
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const [SERVER_ADDRESS, SERVER_PORT] = (0, helpers_1.getServerAddressForProvider)('cursor');
const CURSOR_STATUS_TO_FINISH_REASON = {
finished: 'stop',
error: 'error',
cancelled: 'cancelled',
};
// ---------------------------------------------------------------------------
// Agent creation registry -- links invoke_agent back to create_agent
// ---------------------------------------------------------------------------
class AgentCreationRegistry {
constructor() {
this._contexts = new WeakMap();
}
register(agent, spanContext) {
this._contexts.set(agent, spanContext);
}
get(agent) {
return this._contexts.get(agent);
}
}
const agentRegistry = new AgentCreationRegistry();
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function truncateContent(content) {
const maxLen = config_1.default.maxContentLength;
if (maxLen != null && maxLen > 0 && content.length > maxLen) {
return content.slice(0, maxLen);
}
return content;
}
function mapRunStatusToFinishReason(status) {
if (!status)
return 'stop';
return CURSOR_STATUS_TO_FINISH_REASON[status] || status;
}
function resolveAgentName(options) {
if (!options)
return null;
const name = options.name;
if (name && typeof name === 'string' && name.trim())
return name.trim();
return null;
}
function resolveModelId(options) {
if (!options?.model)
return null;
const model = options.model;
if (typeof model === 'string')
return model;
if (typeof model === 'object' && model.id)
return String(model.id);
return null;
}
function setCommonSpanAttributes(span) {
span.setAttribute(semantic_conventions_1.ATTR_TELEMETRY_SDK_NAME, constant_1.SDK_NAME);
span.setAttribute(semantic_convention_1.default.ATTR_DEPLOYMENT_ENVIRONMENT, config_1.default.environment ?? 'default');
span.setAttribute(semantic_conventions_1.ATTR_SERVICE_NAME, config_1.default.applicationName ?? 'default');
span.setAttribute(semantic_convention_1.default.GEN_AI_SDK_VERSION, constant_1.SDK_VERSION);
span.setAttribute(semantic_convention_1.default.SERVER_ADDRESS, SERVER_ADDRESS);
span.setAttribute(semantic_convention_1.default.SERVER_PORT, SERVER_PORT);
}
// ---------------------------------------------------------------------------
// Tool span tracker -- manages in-flight execute_tool spans from stream
// ---------------------------------------------------------------------------
class ToolSpanTracker {
constructor(tracer, parentSpan, captureContent) {
this._inFlight = new Map();
this._tracer = tracer;
this._parentSpan = parentSpan;
this._captureContent = captureContent;
}
startTool(toolName, callId, args) {
const spanName = `${semantic_convention_1.default.GEN_AI_OPERATION_TYPE_TOOLS} ${toolName}`;
const parentCtx = api_1.trace.setSpan(api_1.context.active(), this._parentSpan);
const span = this._tracer.startSpan(spanName, {
kind: api_1.SpanKind.INTERNAL,
attributes: {
[semantic_convention_1.default.GEN_AI_OPERATION]: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_TOOLS,
[semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL]: semantic_convention_1.default.GEN_AI_SYSTEM_CURSOR,
},
}, parentCtx);
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_NAME, toolName);
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ID, callId);
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_TYPE_OTEL, 'extension');
setCommonSpanAttributes(span);
if (this._captureContent && args != null) {
try {
const argsStr = typeof args === 'string' ? args : JSON.stringify(args);
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_ARGUMENTS, truncateContent(argsStr));
}
catch { /* ignore */ }
}
(0, helpers_1.applyCustomSpanAttributes)(span);
this._inFlight.set(callId, span);
}
endTool(callId, result, isError = false) {
const span = this._inFlight.get(callId);
if (!span)
return;
this._inFlight.delete(callId);
if (isError) {
span.setAttribute(semantic_convention_1.default.ERROR_TYPE, 'ToolExecutionError');
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: 'tool execution failed' });
}
else {
if (this._captureContent && result != null) {
try {
const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_CALL_RESULT, truncateContent(resultStr));
}
catch { /* ignore */ }
}
span.setStatus({ code: api_1.SpanStatusCode.OK });
}
span.end();
}
endAll() {
for (const [, span] of this._inFlight) {
span.setStatus({ code: api_1.SpanStatusCode.OK });
span.end();
}
this._inFlight.clear();
}
}
function processStreamEvent(event, toolTracker, state) {
if (!event || !event.type)
return;
if (!state.runId && event.run_id) {
state.runId = event.run_id;
}
switch (event.type) {
case 'system': {
if (event.model) {
const modelId = typeof event.model === 'string' ? event.model : event.model?.id;
if (modelId)
state.resolvedModel = String(modelId);
}
if (Array.isArray(event.tools) && event.tools.length > 0) {
state.toolDefinitions = event.tools.map(String);
}
break;
}
case 'tool_call': {
const callId = event.call_id;
const toolName = event.name || 'unknown';
const status = event.status;
if (status === 'running') {
toolTracker.startTool(toolName, callId, event.args);
state.toolCalls.push({ name: toolName, callId, args: event.args });
}
else if (status === 'completed') {
toolTracker.endTool(callId, event.result, false);
const tc = state.toolCalls.find(t => t.callId === callId);
if (tc)
tc.result = event.result;
}
else if (status === 'error') {
toolTracker.endTool(callId, event.result, true);
}
break;
}
case 'assistant': {
if (state.firstContentTimeMs === null) {
state.firstContentTimeMs = Date.now();
}
const content = event.message?.content;
if (Array.isArray(content)) {
for (const block of content) {
if (block.type === 'text' && block.text) {
state.assistantText += block.text;
}
}
}
break;
}
case 'thinking': {
if (state.firstContentTimeMs === null) {
state.firstContentTimeMs = Date.now();
}
if (event.text) {
state.thinkingText += event.text;
}
break;
}
}
}
// ---------------------------------------------------------------------------
// Build OTel input/output messages
// ---------------------------------------------------------------------------
function buildInputMessages(message) {
try {
if (typeof message === 'string') {
return JSON.stringify([{
role: 'user',
parts: [{ type: 'text', content: truncateContent(message) }],
}]);
}
const parts = [];
if (message?.text) {
parts.push({ type: 'text', content: truncateContent(message.text) });
}
if (Array.isArray(message?.images)) {
for (const img of message.images) {
parts.push({ type: 'image', mimeType: img.mimeType || 'image/png' });
}
}
if (parts.length === 0)
return null;
return JSON.stringify([{ role: 'user', parts }]);
}
catch {
return null;
}
}
function buildOutputMessages(state, finishReason) {
try {
const parts = [];
if (state.assistantText) {
parts.push({ type: 'text', content: truncateContent(state.assistantText) });
}
if (state.thinkingText) {
parts.push({ type: 'reasoning', content: truncateContent(state.thinkingText) });
}
for (const tc of state.toolCalls) {
const toolPart = {
type: 'tool_call',
id: tc.callId,
name: tc.name,
};
if (tc.args != null) {
toolPart.arguments = typeof tc.args === 'object' ? tc.args : {};
}
parts.push(toolPart);
}
if (parts.length === 0)
return null;
return JSON.stringify([{ role: 'assistant', parts, finish_reason: finishReason }]);
}
catch {
return null;
}
}
// ---------------------------------------------------------------------------
// Emit inference event for invoke_agent spans
// ---------------------------------------------------------------------------
function emitInvokeAgentEvent(span, agentId, model, responseModel, finishReason, inputTokens, outputTokens, inputMessagesJson, outputMessagesJson) {
if (config_1.default.disableEvents)
return;
try {
const attributes = {
[semantic_convention_1.default.GEN_AI_OPERATION]: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AGENT,
[semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL]: semantic_convention_1.default.GEN_AI_SYSTEM_CURSOR,
[semantic_convention_1.default.SERVER_ADDRESS]: SERVER_ADDRESS,
[semantic_convention_1.default.SERVER_PORT]: SERVER_PORT,
};
if (model)
attributes[semantic_convention_1.default.GEN_AI_REQUEST_MODEL] = model;
if (responseModel)
attributes[semantic_convention_1.default.GEN_AI_RESPONSE_MODEL] = responseModel;
if (agentId)
attributes[semantic_convention_1.default.GEN_AI_CONVERSATION_ID] = agentId;
if (finishReason)
attributes[semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON] = [finishReason];
if (inputTokens)
attributes[semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS] = inputTokens;
if (outputTokens)
attributes[semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS] = outputTokens;
if (inputMessagesJson != null) {
attributes[semantic_convention_1.default.GEN_AI_INPUT_MESSAGES] = inputMessagesJson;
}
if (outputMessagesJson != null) {
attributes[semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES] = outputMessagesJson;
}
helpers_1.default.emitInferenceEvent(span, attributes);
}
catch { /* swallow */ }
}
// ---------------------------------------------------------------------------
// Record metrics for invoke_agent spans
// ---------------------------------------------------------------------------
function recordInvokeAgentMetrics(model, inputTokens, outputTokens, cost, duration, errorType) {
if (config_1.default.disableMetrics)
return;
try {
const attributes = {
[semantic_conventions_1.ATTR_TELEMETRY_SDK_NAME]: constant_1.SDK_NAME,
[semantic_conventions_1.ATTR_SERVICE_NAME]: config_1.default.applicationName ?? 'default',
[semantic_convention_1.default.ATTR_DEPLOYMENT_ENVIRONMENT]: config_1.default.environment ?? 'default',
[semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL]: semantic_convention_1.default.GEN_AI_SYSTEM_CURSOR,
[semantic_convention_1.default.GEN_AI_OPERATION]: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AGENT,
[semantic_convention_1.default.SERVER_ADDRESS]: SERVER_ADDRESS,
[semantic_convention_1.default.SERVER_PORT]: SERVER_PORT,
};
if (model)
attributes[semantic_convention_1.default.GEN_AI_REQUEST_MODEL] = model;
if (errorType)
attributes[semantic_convention_1.default.ERROR_TYPE] = errorType;
if (metrics_1.default.genaiClientOperationDuration) {
metrics_1.default.genaiClientOperationDuration.record(duration, attributes);
}
if (inputTokens && metrics_1.default.genaiClientUsageTokens) {
metrics_1.default.genaiClientUsageTokens.record(inputTokens, {
...attributes,
[semantic_convention_1.default.GEN_AI_TOKEN_TYPE]: semantic_convention_1.default.GEN_AI_TOKEN_TYPE_INPUT,
});
}
if (outputTokens && metrics_1.default.genaiClientUsageTokens) {
metrics_1.default.genaiClientUsageTokens.record(outputTokens, {
...attributes,
[semantic_convention_1.default.GEN_AI_TOKEN_TYPE]: semantic_convention_1.default.GEN_AI_TOKEN_TYPE_OUTPUT,
});
}
if (cost && metrics_1.default.genaiCost) {
metrics_1.default.genaiCost.record(cost, attributes);
}
}
catch { /* swallow */ }
}
// ---------------------------------------------------------------------------
// wrapSend -- wraps agent.send() to produce invoke_agent spans
// ---------------------------------------------------------------------------
function wrapSend(tracer, originalSend, agentId, agentName, modelId) {
return function wrappedSend(message, options) {
const captureContent = config_1.default.captureMessageContent ?? true;
const displayName = agentName || agentId;
const spanName = `${semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AGENT} ${displayName}`;
const requestModel = modelId || resolveModelId(options) || 'unknown';
const creationSpanCtx = agentRegistry.get(this);
const links = [];
if (creationSpanCtx) {
links.push({ context: creationSpanCtx });
}
// Start invoke_agent in the same trace as create_agent by using its
// span context as parent. This keeps both spans in one trace while
// the span link provides explicit correlation.
let parentCtx = api_1.context.active();
if (creationSpanCtx) {
const remoteSpan = api_1.trace.wrapSpanContext(creationSpanCtx);
parentCtx = api_1.trace.setSpan(parentCtx, remoteSpan);
}
const span = tracer.startSpan(spanName, {
kind: api_1.SpanKind.CLIENT,
attributes: {
[semantic_convention_1.default.GEN_AI_OPERATION]: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_AGENT,
[semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL]: semantic_convention_1.default.GEN_AI_SYSTEM_CURSOR,
[semantic_convention_1.default.GEN_AI_REQUEST_MODEL]: requestModel,
[semantic_convention_1.default.SERVER_ADDRESS]: SERVER_ADDRESS,
[semantic_convention_1.default.SERVER_PORT]: SERVER_PORT,
},
links,
}, parentCtx);
setCommonSpanAttributes(span);
span.setAttribute(semantic_convention_1.default.GEN_AI_AGENT_ID, agentId);
span.setAttribute(semantic_convention_1.default.GEN_AI_CONVERSATION_ID, agentId);
if (agentName)
span.setAttribute(semantic_convention_1.default.GEN_AI_AGENT_NAME, agentName);
if (captureContent) {
const inputJson = buildInputMessages(message);
if (inputJson)
span.setAttribute(semantic_convention_1.default.GEN_AI_INPUT_MESSAGES, inputJson);
}
(0, helpers_1.applyCustomSpanAttributes)(span);
const startTime = Date.now() / 1000;
const startTimeMs = Date.now();
const usage = { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0 };
const toolTracker = new ToolSpanTracker(tracer, span, captureContent);
const streamState = {
assistantText: '',
thinkingText: '',
toolCalls: [],
resolvedModel: null,
toolDefinitions: null,
runId: null,
firstContentTimeMs: null,
};
const userOnDelta = options?.onDelta;
const mergedOptions = { ...options };
mergedOptions.onDelta = async (args) => {
try {
const update = args?.update;
if (update?.type === 'turn-ended' && update.usage) {
usage.inputTokens += update.usage.inputTokens || 0;
usage.outputTokens += update.usage.outputTokens || 0;
usage.cacheReadTokens += update.usage.cacheReadTokens || 0;
usage.cacheWriteTokens += update.usage.cacheWriteTokens || 0;
}
}
catch { /* swallow */ }
if (userOnDelta) {
return userOnDelta(args);
}
};
const spanContext = api_1.trace.setSpan(api_1.context.active(), span);
(0, helpers_1.setFrameworkLlmActive)();
let runPromise;
try {
runPromise = api_1.context.with(spanContext, () => {
return originalSend.call(this, message, mergedOptions);
});
}
catch (e) {
(0, helpers_1.resetFrameworkLlmActive)();
helpers_1.default.handleException(span, e);
span.end();
throw e;
}
return runPromise.then((run) => {
return createRunProxy(run, tracer, span, startTime, startTimeMs, usage, toolTracker, streamState, captureContent, agentId, agentName, requestModel, message);
}).catch((e) => {
(0, helpers_1.resetFrameworkLlmActive)();
helpers_1.default.handleException(span, e);
recordInvokeAgentMetrics(requestModel, 0, 0, 0, (Date.now() / 1000) - startTime, e?.constructor?.name || '_OTHER');
span.end();
throw e;
});
};
}
// ---------------------------------------------------------------------------
// createRunProxy -- proxies the Run to intercept stream() and wait()
// ---------------------------------------------------------------------------
function createRunProxy(run, tracer, span, startTime, startTimeMs, usage, toolTracker, streamState, captureContent, agentId, agentName, requestModel, message) {
let finalized = false;
let isStreamMode = false;
const finalizeSpan = (result, error) => {
if (finalized)
return;
finalized = true;
(0, helpers_1.resetFrameworkLlmActive)();
toolTracker.endAll();
const duration = (Date.now() / 1000) - startTime;
const status = result?.status || run.status || 'finished';
const finishReason = mapRunStatusToFinishReason(status);
const responseModel = streamState.resolvedModel || result?.model?.id || run.model?.id || null;
const durationMs = result?.durationMs || run.durationMs;
if (responseModel)
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_MODEL, responseModel);
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_FINISH_REASON, [finishReason]);
const runId = streamState.runId || run.id;
if (runId)
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_ID, runId);
if (isStreamMode) {
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_STREAM, true);
}
if (usage.inputTokens)
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_INPUT_TOKENS, usage.inputTokens);
if (usage.outputTokens)
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_OUTPUT_TOKENS, usage.outputTokens);
if (usage.cacheReadTokens)
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, usage.cacheReadTokens);
if (usage.cacheWriteTokens)
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS, usage.cacheWriteTokens);
const effectiveDuration = durationMs ? durationMs / 1000 : duration;
span.setAttribute(semantic_convention_1.default.GEN_AI_CLIENT_OPERATION_DURATION, effectiveDuration);
if (isStreamMode && streamState.firstContentTimeMs !== null) {
const ttft = (streamState.firstContentTimeMs - startTimeMs) / 1000;
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_TIME_TO_FIRST_CHUNK, ttft);
}
if (streamState.toolDefinitions && streamState.toolDefinitions.length > 0) {
span.setAttribute(semantic_convention_1.default.GEN_AI_TOOL_DEFINITIONS, JSON.stringify(streamState.toolDefinitions));
}
const pricingInfo = config_1.default.pricingInfo || {};
const effectiveModel = responseModel || requestModel;
const cost = effectiveModel
? helpers_1.default.getChatModelCost(effectiveModel, pricingInfo, usage.inputTokens, usage.outputTokens)
: 0;
if (cost)
span.setAttribute(semantic_convention_1.default.GEN_AI_USAGE_COST, cost);
let outputMessagesJson = null;
if (captureContent) {
const resultText = result?.result || run.result;
if (resultText && !streamState.assistantText) {
streamState.assistantText = resultText;
}
outputMessagesJson = buildOutputMessages(streamState, finishReason);
if (outputMessagesJson) {
span.setAttribute(semantic_convention_1.default.GEN_AI_OUTPUT_MESSAGES, outputMessagesJson);
}
}
if (error) {
helpers_1.default.handleException(span, error instanceof Error ? error : new Error(String(error)));
}
else if (status === 'error') {
span.setAttribute(semantic_convention_1.default.ERROR_TYPE, 'AgentError');
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: result?.result || 'agent error' });
}
else {
span.setStatus({ code: api_1.SpanStatusCode.OK });
}
const inputMessagesJson = captureContent ? buildInputMessages(message) : null;
emitInvokeAgentEvent(span, agentId, requestModel, responseModel, finishReason, usage.inputTokens, usage.outputTokens, inputMessagesJson, outputMessagesJson);
recordInvokeAgentMetrics(requestModel, usage.inputTokens, usage.outputTokens, cost, duration, error ? (error.constructor?.name || '_OTHER') : (status === 'error' ? 'AgentError' : undefined));
span.end();
};
return new Proxy(run, {
get(target, prop, receiver) {
if (prop === 'stream') {
const originalStream = target.stream;
if (typeof originalStream !== 'function')
return originalStream;
return function (...streamArgs) {
isStreamMode = true;
const generator = originalStream.apply(target, streamArgs);
return wrapAsyncGenerator(generator, toolTracker, streamState, finalizeSpan);
};
}
if (prop === 'wait') {
const originalWait = target.wait;
if (typeof originalWait !== 'function')
return originalWait;
return function (...waitArgs) {
return originalWait.apply(target, waitArgs).then((result) => {
finalizeSpan(result);
return result;
}).catch((e) => {
finalizeSpan(undefined, e);
throw e;
});
};
}
if (prop === 'cancel') {
const originalCancel = target.cancel;
if (typeof originalCancel !== 'function')
return originalCancel;
return function (...cancelArgs) {
return originalCancel.apply(target, cancelArgs).then((result) => {
finalizeSpan({ status: 'cancelled' });
return result;
});
};
}
return Reflect.get(target, prop, receiver);
},
});
}
// ---------------------------------------------------------------------------
// wrapAsyncGenerator -- wraps run.stream() to intercept SDKMessage events
// ---------------------------------------------------------------------------
async function* wrapAsyncGenerator(generator, toolTracker, streamState, finalizeSpan) {
try {
for await (const event of generator) {
try {
processStreamEvent(event, toolTracker, streamState);
}
catch { /* swallow processing errors */ }
yield event;
}
finalizeSpan();
}
catch (e) {
finalizeSpan(undefined, e);
throw e;
}
}
// ---------------------------------------------------------------------------
// patchAgentCreate -- wraps Agent.create() for create_agent spans
// ---------------------------------------------------------------------------
function patchAgentCreate(tracer) {
return (originalCreate) => {
return async function wrappedCreate(options) {
const agentName = resolveAgentName(options);
const modelId = resolveModelId(options);
const displayName = agentName || 'cursor-agent';
const spanName = `${semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CREATE_AGENT} ${displayName}`;
const span = tracer.startSpan(spanName, {
kind: api_1.SpanKind.CLIENT,
attributes: {
[semantic_convention_1.default.GEN_AI_OPERATION]: semantic_convention_1.default.GEN_AI_OPERATION_TYPE_CREATE_AGENT,
[semantic_convention_1.default.GEN_AI_PROVIDER_NAME_OTEL]: semantic_convention_1.default.GEN_AI_SYSTEM_CURSOR,
[semantic_convention_1.default.SERVER_ADDRESS]: SERVER_ADDRESS,
[semantic_convention_1.default.SERVER_PORT]: SERVER_PORT,
},
});
setCommonSpanAttributes(span);
if (agentName)
span.setAttribute(semantic_convention_1.default.GEN_AI_AGENT_NAME, agentName);
if (modelId)
span.setAttribute(semantic_convention_1.default.GEN_AI_REQUEST_MODEL, modelId);
(0, helpers_1.applyCustomSpanAttributes)(span);
try {
const agent = await originalCreate.call(this, options);
const agentId = agent.agentId;
if (agentId) {
span.setAttribute(semantic_convention_1.default.GEN_AI_AGENT_ID, agentId);
}
agentRegistry.register(agent, span.spanContext());
const resolvedModel = agent.model?.id || modelId;
if (resolvedModel)
span.setAttribute(semantic_convention_1.default.GEN_AI_RESPONSE_MODEL, resolvedModel);
span.setStatus({ code: api_1.SpanStatusCode.OK });
span.end();
if (typeof agent.send === 'function') {
const originalAgentSend = agent.send.bind(agent);
agent.send = wrapSend(tracer, originalAgentSend, agentId, agentName, resolvedModel);
}
return agent;
}
catch (e) {
helpers_1.default.handleException(span, e);
span.end();
throw e;
}
};
};
}
// ---------------------------------------------------------------------------
// patchAgentResume -- wraps Agent.resume() to patch send() on resumed agents
// ---------------------------------------------------------------------------
function patchAgentResume(tracer) {
return (originalResume) => {
return async function wrappedResume(agentId, options) {
const agentName = resolveAgentName(options);
const modelId = resolveModelId(options);
const agent = await originalResume.call(this, agentId, options);
const resolvedAgentId = agent.agentId || agentId;
const resolvedModel = agent.model?.id || modelId;
if (typeof agent.send === 'function') {
const originalAgentSend = agent.send.bind(agent);
agent.send = wrapSend(tracer, originalAgentSend, resolvedAgentId, agentName, resolvedModel);
}
return agent;
};
};
}
//# sourceMappingURL=wrapper.js.map