/**
 * OODA Skill Registry — interface only (sprint-525:quick-1 foundation).
 *
 * Implementations land in sprint-525:quick-5. Skills are pure (or
 * side-effect-bearing) callable units invoked from inside OODA phase
 * functions. The `dryRunMocks` map lets tests + dry-run mode replace
 * any skill execution with a deterministic mock keyed by skill name.
 *
 * The SkillRegistry is intentionally untyped at the value level to keep
 * the SDK barrel compatible with arbitrary downstream skill libraries.
 * A typed lookup helper can be layered on top in 0.7.x.
 *
 * @public
 */
/**
 * Error thrown by {@link InMemorySecretsAccessor} when a secret is requested
 * without a default value and is not configured.
 *
 * @public @since 1.2.0
 */
export class SecretNotFoundError extends Error {
    secretName;
    constructor(secretName) {
        super(`secret '${secretName}' is not configured`);
        this.name = "SecretNotFoundError";
        this.secretName = secretName;
    }
}
/**
 * In-memory {@link SecretsAccessor} suitable for tests and ephemeral
 * runtimes. Production hosts can implement their own accessor backed by
 * Vault, SOPS, or k8s SealedSecrets while preserving the audit semantics.
 *
 * @public @since 1.2.0
 */
export class InMemorySecretsAccessor {
    store;
    accessed = new Set();
    constructor(secrets = {}) {
        this.store = new Map(Object.entries(secrets));
    }
    get(name, defaultValue) {
        this.accessed.add(name);
        const value = this.store.get(name);
        if (value !== undefined)
            return value;
        if (defaultValue !== undefined)
            return defaultValue;
        throw new SecretNotFoundError(name);
    }
    has(name) {
        this.accessed.add(name);
        return this.store.has(name);
    }
    get accessedSecrets() {
        return this.accessed;
    }
}
/**
 * Noop accessor — used as default for skills that declare no secrets.
 * Always returns `defaultValue` when provided, otherwise throws.
 *
 * @public @since 1.2.0
 */
export const NOOP_SECRETS = new InMemorySecretsAccessor();
/**
 * Build a {@link SkillContext} with sane defaults for the optional fields.
 * Useful for hosts that want a uniform context without writing the boilerplate.
 *
 * Time accessors are LIVE — `elapsedSeconds` and `remainingSeconds` are
 * recomputed on each property read, so a skill that loops can observe them
 * decreasing without rebuilding the context.
 *
 * @public @since 1.2.0
 */
export function createSkillContext(opts) {
    const startedAt = opts.startedAt ?? new Date();
    const timeoutSeconds = opts.timeoutSeconds ?? Number.POSITIVE_INFINITY;
    const secrets = opts.secrets ?? NOOP_SECRETS;
    const progressFn = opts.progress ?? noopProgress;
    const ctx = {
        isReplay: opts.isReplay ?? false,
        dryRunMocks: opts.dryRunMocks ?? {},
        db: opts.db,
        logger: opts.logger,
        secrets,
        progress: (value, message) => {
            const clamped = clampProgress(value);
            progressFn(clamped, message);
        },
        get elapsedSeconds() {
            return Math.max(0, (Date.now() - startedAt.getTime()) / 1000);
        },
        get remainingSeconds() {
            if (!Number.isFinite(timeoutSeconds))
                return Number.POSITIVE_INFINITY;
            const elapsed = (Date.now() - startedAt.getTime()) / 1000;
            return Math.max(0, timeoutSeconds - elapsed);
        },
    };
    if (opts.executionId !== undefined) {
        Object.defineProperty(ctx, "executionId", {
            value: opts.executionId,
            writable: false,
            enumerable: true,
            configurable: false,
        });
    }
    return ctx;
}
function clampProgress(value) {
    if (!Number.isFinite(value))
        return 0;
    if (value < 0)
        return 0;
    if (value > 1)
        return 1;
    return value;
}
const noopProgress = () => { };
/**
 * Empty registry — convenience for tests and minimal agents.
 */
export const EMPTY_SKILL_REGISTRY = Object.freeze({});
//# sourceMappingURL=skills.js.map