agents
Version:
A home for your AI agents
204 lines (203 loc) • 7.15 kB
JavaScript
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