UNPKG

agents

Version:

A home for your AI agents

204 lines (203 loc) 7.15 kB
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js"; import { AsyncLocalStorage } from "node:async_hooks"; import * as cloudflareWorkers from "cloudflare:workers"; //#region src/observability/tracing/tracer.ts /** * Spans that asked to be invocation-bounded while a scope was active. Closed * when that scope ends; see {@link withInvocationScope}. */ const invocationScope = new AsyncLocalStorage(); /** * Runs `body` as one traced invocation. * * Work that escapes its native invocation cannot be traced from the context it * started in: the still-open span is force-closed against the invocation that * owned that context, which reports a negative duration or `span_not_ended`. * Spans opened with {@link SpanLifetime.boundToInvocation} inside this scope * are therefore closed before `body` settles — but not one moment earlier, so * everything that completes during the invocation (the normal case: a chat * turn is awaited by the handler that received it) still records its finish * attributes. A span truncated this way is marked * `cloudflare.agents.span.truncated` rather than passing as complete. */ function withInvocationScope(body, options) { const current = invocationScope.getStore(); if (options?.detached !== true && current !== void 0 && !current.ended) return body(); const scope = { ended: false, open: /* @__PURE__ */ new Set() }; const endInvocation = () => { scope.ended = true; for (const span of scope.open) try { span.truncate(); } catch {} scope.open.clear(); }; return invocationScope.run(scope, () => { let result; try { result = body(); } catch (cause) { endInvocation(); throw cause; } if (isPromiseLike(result)) return Promise.resolve(result).finally(endInvocation); endInvocation(); return result; }); } /** Creates a tracer from a runtime span capability. */ function createTracer(runtime) { return new RuntimeTracer(runtime); } var RuntimeTracer = class { constructor(runtime) { this.runtime = runtime; } withSpan(name, attributes, run, lifetime) { return this.activate(name, attributes, (span) => { const outOfBand = lifetime?.boundToInvocation === true && bindToInvocation(span); const result = run(span); if (isPromiseLike(result)) { if (outOfBand) span.truncate(); return Promise.resolve(result).catch((cause) => { span.fail(cause); throw cause; }).finally(() => { span.close(); }); } if (outOfBand) span.truncate(); span.close(); return result; }); } openSpan(name, attributes, activate, lifetime) { if (!lifetime?.boundToInvocation) return this.activate(name, attributes, activate); return this.activate(name, attributes, (span) => { const invocationEnded = bindToInvocation(span); try { return activate(span); } finally { if (invocationEnded) span.truncate(); } }); } /** * Shared scaffold: opens an active span, seeds its attributes, and fails the * span on a thrown defect before rethrowing. The `body` decides the span's * finishing policy (managed vs. caller-owned). */ activate(name, attributes, body) { return this.runtime.startActiveSpan(name, (writer) => { setAttributes(writer, attributes); const span = new ManagedSpan(writer); try { return body(span); } catch (cause) { span.fail(cause); throw cause; } }); } }; var _closed = /* @__PURE__ */ new WeakMap(); var ManagedSpan = class { constructor(span) { this.span = span; _classPrivateFieldInitSpec(this, _closed, false); } get isTraced() { return this.span.isTraced; } /** INTERNAL: see {@link writeSpanAttributes}. */ writeAttributes(attributes) { if (_classPrivateFieldGet2(_closed, this)) return; setAttributes(this.span, attributes); } finish(attributes = {}) { if (_classPrivateFieldGet2(_closed, this)) return; setAttributes(this.span, attributes); this.close(); } fail(cause) { if (_classPrivateFieldGet2(_closed, this)) return; if (isCancellation(cause)) setAttributes(this.span, { "cloudflare.agents.canceled": true }); else setAttributes(this.span, { "error.type": cause instanceof Error ? cause.name || "Error" : typeof cause }); this.close(); } close() { if (_classPrivateFieldGet2(_closed, this)) return; _classPrivateFieldSet2(_closed, this, true); this.span.end(); } /** * Closes a span whose work has not finished because its invocation is ending. * The marker distinguishes "this span has no tokens because the turn escaped * its invocation" from "this span completed and reported none". */ truncate() { if (_classPrivateFieldGet2(_closed, this)) return; setAttributes(this.span, { "cloudflare.agents.span.truncated": true }); this.close(); } }; /** * Registers a span for closure at the end of the current invocation, and * reports whether that invocation has already ended. * * Outside any scope the span keeps its natural lifetime: with no known * invocation boundary there is nothing to bound it to, and closing early would * discard finish attributes for no gain. Inside a scope that has already * ended — work resumed later while still carrying this context — the span * cannot outlive an invocation that is already gone, so the caller truncates * it as soon as it has written what it knows. */ function bindToInvocation(span) { const scope = invocationScope.getStore(); if (scope === void 0) return false; if (scope.ended) return true; scope.open.add(span); return false; } /** * INTERNAL: writes attributes onto an open managed span. Lets instrumentation * defer expensive attribute computation until after the isTraced check (span * names must exist at open time; attributes need not). Not part of the public * barrel surface. */ function writeSpanAttributes(span, attributes) { if (span instanceof ManagedSpan) span.writeAttributes(attributes); } function setAttributes(span, attributes) { if (!span.isTraced) return; try { for (const [key, value] of Object.entries(attributes)) if (value !== void 0) span.setAttribute(key, value); } catch {} } function isPromiseLike(value) { return value !== null && value !== void 0 && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function"; } /** * Recognizes caller/runtime cancellation (an `AbortError`, e.g. from an aborted * `AbortSignal`) so it can be classified separately from genuine failures. A * `DOMException` named `AbortError` is not always an `Error` instance, so this * probes the `name` field structurally rather than via `instanceof`. */ function isCancellation(cause) { return typeof cause === "object" && cause !== null && "name" in cause && cause.name === "AbortError"; } //#endregion //#region src/observability/tracing/cloudflare.ts const noopSpan = { isTraced: false, setAttribute() {}, end() {} }; const tracer = createTracer(cloudflareWorkers.tracing ?? { startActiveSpan(_name, run) { return run(noopSpan); } }); //#endregion export { withInvocationScope as n, writeSpanAttributes as r, tracer as t }; //# sourceMappingURL=cloudflare-BduZwmYK.js.map