/**
 * Async request-scoped context for Neotoma write-path attribution.
 *
 * The MCP `/mcp` endpoint receives AAuth-verified requests plus OAuth /
 * `clientInfo` fallback data, but writes happen deep inside services
 * (observation_storage, relationships, timeline_events, …) that live many
 * call frames below the HTTP handler. Passing an `AgentIdentity` through
 * every function signature would touch dozens of files; an
 * {@link AsyncLocalStorage} store propagates the same information without
 * any plumbing at call sites.
 *
 * Contract:
 * - The HTTP transport handler wraps each request in
 *   {@link runWithRequestContext} *before* dispatching into the MCP server.
 * - Stdio transport does not set a context; write-path services treat the
 *   absence of context the same as "anonymous / CLI-local" attribution.
 * - Nested writes inherit the outer context (which is exactly what we want:
 *   a single store_structured call that writes sources + observations +
 *   relationships should stamp them all with the same attribution).
 */
import { AsyncLocalStorage } from "node:async_hooks";
import { toAttributionProvenance } from "../crypto/agent_identity.js";
const storage = new AsyncLocalStorage();
/**
 * Run `fn` with the given {@link RequestContext} active. Any nested async
 * work (awaits, promise chains, timers) inherits the context via Node's
 * async_hooks machinery.
 */
export function runWithRequestContext(context, fn) {
    return storage.run(context, fn);
}
/** Fetch the current request context, or `null` outside of one. */
export function getRequestContext() {
    return storage.getStore() ?? null;
}
/**
 * Shorthand that returns the active agent identity, or `null` when no
 * context is active or the context has no identity.
 */
export function getCurrentAgentIdentity() {
    return storage.getStore()?.agentIdentity ?? null;
}
/**
 * Shorthand that returns the {@link AttributionProvenance} block for the
 * current request. Returns an empty object when no context / identity is
 * active, making it safe to spread into provenance JSON regardless.
 */
export function getCurrentAttribution() {
    const store = storage.getStore();
    return toAttributionProvenance(store?.agentIdentity ?? null, store?.externalActor ?? null);
}
/**
 * Return the external actor from the active request context, if set.
 */
export function getCurrentExternalActor() {
    return storage.getStore()?.externalActor ?? null;
}
/** Read the middleware-stashed decision from the active async context. */
export function getCurrentAttributionDecision() {
    return storage.getStore()?.attributionDecision ?? null;
}
/**
 * Read the AAuth admission record (set by the admission service in the
 * HTTP middleware chain) from the active async context. Returns `null`
 * when admission did not run for this request.
 */
export function getCurrentAAuthAdmission() {
    return storage.getStore()?.aauthAdmission ?? null;
}
/**
 * Run `fn` with an {@link ExternalActor} attached to the current request
 * context. If a context already exists it is cloned with the actor slot
 * set; if no context is active a minimal one is created. Existing
 * `agentIdentity`, `attributionDecision`, and `aauthAdmission` are
 * preserved so this helper composes safely within the existing pipeline.
 */
export function runWithExternalActor(actor, fn) {
    const existing = storage.getStore();
    const merged = {
        agentIdentity: existing?.agentIdentity ?? null,
        attributionDecision: existing?.attributionDecision ?? null,
        aauthAdmission: existing?.aauthAdmission ?? null,
        externalActor: actor,
        bypassGuestStoreAccessPolicy: existing?.bypassGuestStoreAccessPolicy ?? false,
    };
    return storage.run(merged, fn);
}
//# sourceMappingURL=request_context.js.map