agents
Version:
A home for your AI agents
416 lines (415 loc) • 17.9 kB
JavaScript
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, t as _classPrivateFieldGet2 } from "../classPrivateFieldGet2-DZBYAB34.js";
import { t as nativeTracing } from "../cloudflare-CUUm_9q9.js";
import { t as channels } from "../diagnostics-BzvaX2UT.js";
import { t as genericObservability } from "../emitter-DPOVcCln.js";
import { AsyncLocalStorage } from "node:async_hooks";
import { subscribe as subscribe$1, unsubscribe } from "node:diagnostics_channel";
import { INVALID_SPAN_CONTEXT, ROOT_CONTEXT, SpanStatusCode, context, createContextKey, diag, isSpanContextValid, trace } from "@opentelemetry/api";
//#region src/observability/otel/bridge.ts
/**
* The OpenTelemetry API on Workers native tracing.
*
* Every recording OTel span is a native span. Native tracing has no manual
* parent wiring: a span's parent is whatever native span is active (in the
* async context) when it opens, and a span can only be active inside the
* callback that opened it. The bridge works within that:
*
* - Every span opens with `tracing.startActiveSpan`, so it is native-active for
* at least a moment. Inside that moment the bridge captures an async-context
* snapshot: a way back into "this span is the active native parent".
* - `startActiveSpan` runs the caller's function inside the native callback.
* - `startSpan` returns from the native callback at once (the span stays open
* and inactive, as OTel requires). When the span later becomes active through
* `context.with(trace.setSpan(ctx, span), fn)` — the pattern the MCP SDK and
* other instrumentation use — `fn` runs inside the snapshot, so platform
* spans (fetch, KV, D1, Durable Objects) and child spans nest under it.
* - A span is only opened where its native parent is the span the caller asked
* for. Anything native tracing cannot represent faithfully (remote parents,
* new roots, explicit start times, parents from another request) returns a
* non-recording span instead of a span under a substitute parent.
*/
const reported = /* @__PURE__ */ new Set();
/**
* Marks a context taken where no native span was visible. Durable Object
* constructors and `blockConcurrencyWhile` callbacks report no active span,
* yet native spans opened there still parent to the request; at module scope
* they record nothing. Either way the runtime, not the bridge, picks.
*/
const AMBIENT = createContextKey("agents: ambient native span");
/**
* Reports a dropped or refused feature once per isolate through OTel's `diag`
* logger. Messages name the feature only, never span names or values.
*/
function unsupported(feature) {
if (reported.has(feature)) return;
reported.add(feature);
diag.warn(`agents/observability: ${feature} cannot be represented by Workers tracing.`);
}
/** Telemetry must never replace the application's result or error. */
function safely(write, feature) {
try {
write();
} catch {
unsupported(`${feature} (the runtime rejected the value)`);
}
}
var _spans = /* @__PURE__ */ new WeakMap();
var _tracer = /* @__PURE__ */ new WeakMap();
var _snapshot = /* @__PURE__ */ new WeakMap();
var Bridge = class {
constructor(native, snapshot) {
this.native = native;
_classPrivateFieldInitSpec(this, _spans, /* @__PURE__ */ new WeakMap());
_classPrivateFieldInitSpec(this, _tracer, void 0);
_classPrivateFieldInitSpec(this, _snapshot, void 0);
_classPrivateFieldSet2(_snapshot, this, snapshot);
this.contextManager = new WorkersContextManager(this).enable();
_classPrivateFieldSet2(_tracer, this, new WorkersTracer(this));
const tracer = _classPrivateFieldGet2(_tracer, this);
this.tracerProvider = { getTracer: () => tracer };
}
/** The OTel span for a native span the bridge did not open (root, agent spans). */
wrap(native) {
let span = _classPrivateFieldGet2(_spans, this).get(native);
if (span === void 0) {
span = new WorkersSpan(this, native, void 0);
_classPrivateFieldGet2(_spans, this).set(native, span);
}
return span;
}
/**
* Opens a native span where `placement` says, seeds its attributes, and
* calls `body` while it is the active native span.
*/
open(name, attributes, placement, body) {
const start = () => this.native.startActiveSpan(name, (native) => {
const span = new WorkersSpan(this, native, _classPrivateFieldGet2(_snapshot, this).call(this));
_classPrivateFieldGet2(_spans, this).set(native, span);
if (attributes !== void 0) span.setAttributes(attributes);
return body(span);
});
return placement.in === void 0 ? start() : placement.in(start);
}
/**
* Decides whether a span can open natively under the requested parent, and
* from which async context.
*/
place(options, parentContext) {
const parent = options.root ? void 0 : trace.getSpan(parentContext);
const current = this.native.getActiveSpan();
const refuse = (feature) => {
unsupported(feature);
const ids = parent?.spanContext();
return { refused: ids !== void 0 && isSpanContextValid(ids) ? ids : INVALID_SPAN_CONTEXT };
};
if (current === void 0) return parent === void 0 && !options.root && options.startTime === void 0 && parentContext.getValue(AMBIENT) === true ? { in: void 0 } : refuse("Spans outside a request");
if (parent === void 0) return refuse("Root spans and spans without a parent");
if (!(parent instanceof WorkersSpan) || parent.bridge !== this) return refuse("Spans under remote or foreign parents");
if (options.startTime !== void 0) return refuse("Explicit span start timestamps");
if (options.links !== void 0 && options.links.length > 0) unsupported("Span links (dropped)");
if (parent.native === current) return { in: void 0 };
const reentry = parent.reentry(current);
return reentry === void 0 ? refuse("Spans under a parent from another request or scope") : { in: reentry };
}
/** Runs `fn` with `context` as the OTel context and `native` as its native span. */
run(context, native, fn) {
return this.contextManager.run({
context,
native
}, fn);
}
};
var _traceId = /* @__PURE__ */ new WeakMap();
var WorkersSpan = class {
constructor(bridge, native, snapshot) {
this.bridge = bridge;
this.native = native;
this.snapshot = snapshot;
_classPrivateFieldInitSpec(this, _traceId, void 0);
_classPrivateFieldSet2(_traceId, this, snapshot === void 0 ? void 0 : traceIdOf(native));
}
/**
* The snapshot to re-enter so this span is the active native parent again,
* or `undefined` when that would cross into another request.
*/
reentry(current) {
if (this.snapshot === void 0) return;
const currentTraceId = traceIdOf(current);
if (_classPrivateFieldGet2(_traceId, this) !== void 0 && currentTraceId !== void 0 && _classPrivateFieldGet2(_traceId, this) !== currentTraceId) return;
return this.snapshot;
}
spanContext() {
let ids;
safely(() => {
ids = this.native.spanContext?.();
}, "Span context");
if (ids === void 0) return INVALID_SPAN_CONTEXT;
const { traceId, spanId, traceFlags } = ids;
return {
traceId,
spanId,
traceFlags
};
}
isRecording() {
return this.native.isTraced;
}
setAttribute(key, value) {
if (key === "" || value === void 0 || !this.native.isTraced) return this;
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") safely(() => this.native.setAttribute(key, value), "Attribute");
else unsupported("Array and non-scalar attribute values (dropped)");
return this;
}
setAttributes(attributes) {
for (const [key, value] of Object.entries(attributes)) this.setAttribute(key, value);
return this;
}
setStatus(status) {
if (status.code !== SpanStatusCode.OK && status.code !== SpanStatusCode.ERROR) return this;
const setStatus = this.native.setStatus;
if (setStatus === void 0) {
unsupported("Span status on this runtime version");
return this;
}
safely(() => setStatus.call(this.native, status.code === SpanStatusCode.ERROR ? status.message === void 0 ? { code: "error" } : {
code: "error",
message: status.message
} : { code: "ok" }), "Span status");
return this;
}
updateName(name) {
const updateName = this.native.updateName;
if (updateName === void 0) {
unsupported("Span renames on this runtime version");
return this;
}
safely(() => updateName.call(this.native, name), "Span rename");
return this;
}
recordException(exception, time) {
if (time !== void 0) unsupported("Explicit exception timestamps (recorded at runtime time)");
safely(() => this.native.recordException(exception), "Exception");
}
end(endTime) {
if (endTime !== void 0) unsupported("Explicit end timestamps (ended at runtime time)");
safely(() => this.native.end(), "Span end");
}
addEvent(_name, _attributesOrStartTime, _startTime) {
unsupported("Span events other than exceptions (dropped)");
return this;
}
addLink(_link) {
unsupported("Span links (dropped)");
return this;
}
addLinks(links) {
if (links.length > 0) unsupported("Span links (dropped)");
return this;
}
};
function traceIdOf(native) {
try {
const traceId = native.spanContext?.().traceId;
return traceId === void 0 || /^0+$/.test(traceId) ? void 0 : traceId;
} catch {
return;
}
}
var WorkersTracer = class {
constructor(bridge) {
this.bridge = bridge;
}
startSpan(name, options = {}, parentContext = this.bridge.contextManager.active()) {
const placement = this.bridge.place(options, parentContext);
if ("refused" in placement) return trace.wrapSpanContext(placement.refused);
return this.bridge.open(name, options.attributes, placement, (span) => span);
}
startActiveSpan(name, optionsOrFn, contextOrFn, maybeFn) {
const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
const parentContext = typeof contextOrFn === "object" ? contextOrFn : this.bridge.contextManager.active();
const fn = typeof optionsOrFn === "function" ? optionsOrFn : typeof contextOrFn === "function" ? contextOrFn : maybeFn;
if (fn === void 0) throw new TypeError("startActiveSpan requires a callback");
const placement = this.bridge.place(options, parentContext);
if ("refused" in placement) {
const span = trace.wrapSpanContext(placement.refused);
return this.bridge.contextManager.with(trace.setSpan(parentContext, span), () => fn(span));
}
return this.bridge.open(name, options.attributes, placement, (span) => this.bridge.run(trace.setSpan(parentContext, span), span.native, () => fn(span)));
}
};
var _storage = /* @__PURE__ */ new WeakMap();
var WorkersContextManager = class {
constructor(bridge) {
this.bridge = bridge;
_classPrivateFieldInitSpec(this, _storage, void 0);
}
active() {
const storage = _classPrivateFieldGet2(_storage, this);
if (storage === void 0) return ROOT_CONTEXT;
const scope = storage.getStore();
const native = this.bridge.native.getActiveSpan();
if (scope !== void 0 && scope.native === native) return scope.context;
if (native === void 0) return scope?.context ?? ROOT_CONTEXT.setValue(AMBIENT, true);
return trace.setSpan(scope?.context ?? ROOT_CONTEXT, this.bridge.wrap(native));
}
with(context, fn, thisArg, ...args) {
const call = () => fn.apply(thisArg, args);
if (_classPrivateFieldGet2(_storage, this) === void 0) return call();
const current = this.bridge.native.getActiveSpan();
const span = trace.getSpan(context);
if (span instanceof WorkersSpan && span.bridge === this.bridge && current !== void 0 && span.native !== current) {
const reentry = span.reentry(current);
if (reentry !== void 0) return reentry(() => this.run({
context,
native: span.native
}, call));
unsupported("Activating a span from another request or scope");
}
return this.run({
context,
native: current
}, call);
}
/** INTERNAL: runs `fn` in `scope` without native activation. */
run(scope, fn) {
const storage = _classPrivateFieldGet2(_storage, this);
return storage === void 0 ? fn() : storage.run(scope, fn);
}
bind(context, target) {
if (typeof target !== "function") {
unsupported("context.bind on non-functions (left unchanged)");
return target;
}
const manager = this;
const callable = target;
return function(...args) {
return manager.with(context, () => callable.apply(this, args));
};
}
enable() {
_classPrivateFieldGet2(_storage, this) ?? _classPrivateFieldSet2(_storage, this, new AsyncLocalStorage());
return this;
}
/** Workers has no `AsyncLocalStorage.disable()`; drop access to old scopes instead. */
disable() {
_classPrivateFieldSet2(_storage, this, void 0);
return this;
}
};
/** Creates (but does not register) the OTel components for native tracing. */
function createOpenTelemetryBridge(runtime) {
return new Bridge(runtime.tracing, runtime.snapshot ?? (() => AsyncLocalStorage.snapshot()));
}
//#endregion
//#region src/observability/otel/register.ts
/** Where `@opentelemetry/api` 1.x keeps its global registrations. */
const OTEL_GLOBAL = Symbol.for("opentelemetry.js.api.1");
function otelGlobal() {
return globalThis[OTEL_GLOBAL];
}
/** The provider behind the global proxy, when the proxy exposes it. */
function delegateOf(provider) {
const proxy = provider;
return typeof proxy?.getDelegate === "function" ? proxy.getDelegate() : provider;
}
/**
* Registers `bridge` as the global OTel context manager and tracer provider.
*
* - Idempotent while the bridge is registered.
* - Steps aside (`"deferred"`) when both globals are already registered by
* someone else: an OTel SDK the application set up, or the Workers runtime
* once it registers the OTel API natively. The existing setup keeps working.
* - Throws when only one of the two is registered, or the registry belongs to
* an incompatible `@opentelemetry/api` copy: either would leave OTel spans
* silently detached from native tracing.
*/
function registerBridge(bridge) {
const registry = otelGlobal();
const contextOwner = registry?.context;
const traceOwner = registry?.trace;
const ownsContext = contextOwner === bridge.contextManager;
const ownsTrace = traceOwner !== void 0 && delegateOf(traceOwner) === bridge.tracerProvider;
if (ownsContext && ownsTrace) return "already-registered";
if (contextOwner !== void 0 && traceOwner !== void 0 && !ownsContext && !ownsTrace) {
diag.info("agents/observability: OpenTelemetry is already registered; experimental_registerOpenTelemetry() left it in place.");
return "deferred";
}
if (contextOwner !== void 0 || traceOwner !== void 0) throw new Error(`experimental_registerOpenTelemetry(): an OpenTelemetry ${contextOwner === void 0 ? "tracer provider" : "context manager"} is already registered without its ${contextOwner === void 0 ? "context manager" : "tracer provider"}. Remove the partial OpenTelemetry setup, or register both before calling experimental_registerOpenTelemetry().`);
if (!context.setGlobalContextManager(bridge.contextManager)) throw incompatible(registry?.version);
bridge.contextManager.enable();
if (!trace.setGlobalTracerProvider(bridge.tracerProvider)) {
context.disable();
throw incompatible(registry?.version);
}
return "registered";
}
function incompatible(version) {
return /* @__PURE__ */ new Error(`experimental_registerOpenTelemetry(): the global OpenTelemetry API rejected the registration${version === void 0 ? "" : ` (globals were created by /api ${version})`}. Install a single version of /api.`);
}
//#endregion
//#region src/observability/otel/index.ts
let bridge;
/**
* Connects the OpenTelemetry API (`@opentelemetry/api`) to Workers tracing.
* Call once at module scope:
*
* ```ts
* import { experimental_registerOpenTelemetry } from "agents/observability";
*
* experimental_registerOpenTelemetry();
* ```
*
* Afterwards, libraries instrumented with the OpenTelemetry API (the MCP SDK,
* AI SDK telemetry, your own `trace.getTracer(...)` spans) record native
* Workers spans. They nest with the Agents SDK's spans and the automatic
* fetch, KV, D1 and Durable Object spans, and export through
* `observability.traces.destinations`. Enable `observability.traces` in your
* Wrangler configuration for anything to be recorded.
*
* Safe to call more than once. When OpenTelemetry is already fully registered,
* by your own SDK setup or natively by the Workers runtime, this leaves it in
* place and does nothing. Throws when only part of an OpenTelemetry setup is
* registered.
*
* @experimental Undocumented and subject to change: a temporary bridge until
* Workers supports the OpenTelemetry API natively. See
* `design/rfc-opentelemetry-bridge.md` for what it records and refuses.
*/
function experimental_registerOpenTelemetry() {
if (nativeTracing === void 0) {
diag.warn("agents/observability: this runtime has no native tracing; experimental_registerOpenTelemetry() did nothing.");
return;
}
bridge ??= createOpenTelemetryBridge({ tracing: nativeTracing });
registerBridge(bridge);
}
//#endregion
//#region src/observability/index.ts
/**
* Channel keys whose diagnostics channel name differs from `agents:${key}`.
* Keep this in sync with {@link channels} for any camelCase key that maps to a
* snake_case diagnostics channel.
*/
const CHANNEL_DIAGNOSTIC_NAME_OVERRIDES = { agentTool: "agents:agent_tool" };
/**
* Subscribe to a typed observability channel.
*
* ```ts
* import { subscribe } from "agents/observability";
*
* const unsub = subscribe("rpc", (event) => {
* console.log(event.payload.method); // fully typed
* });
* ```
*
* @returns A function that unsubscribes the callback.
*/
function subscribe(channelKey, callback) {
const name = CHANNEL_DIAGNOSTIC_NAME_OVERRIDES[channelKey] ?? `agents:${channelKey}`;
const handler = (message, _name) => callback(message);
subscribe$1(name, handler);
return () => unsubscribe(name, handler);
}
//#endregion
export { channels, experimental_registerOpenTelemetry, genericObservability, subscribe };
//# sourceMappingURL=index.js.map