/**
 * Function-library registry: typed `FunctionDef` + name-keyed dispatcher.
 *
 * Architecture (per `project_opensquid_modular_function_skill_separation`):
 * the runtime ships atomic primitives (regex, state I/O, LLM calls, verdicts);
 * skills compose them into rule processes via YAML. The registry is the ONLY
 * path from a skill step to a primitive — no global `import { add }`
 * shortcuts. The evaluator (Task 1.3) drives every call through `call()`.
 *
 * Each `FunctionDef` carries its own Zod arg schema. The registry validates
 * args before invoking `execute`, so primitives see only well-typed input
 * and never have to re-check what the YAML claimed it was passing.
 *
 * Error model: throws are reserved for programmer errors at startup
 * (duplicate registration). Runtime failures travel as `Result<T, FunctionError>`
 * — see `src/runtime/result.ts`. Primitives MUST NOT throw inside `execute`;
 * the evaluator catches stray throws and wraps them in `{ kind: 'runtime' }`,
 * but that path is for bugs, not for normal failure modes.
 *
 * Imports from: zod, runtime/types.ts, runtime/result.ts.
 * Imported by: runtime/ (evaluator), packs/ (when skills resolve `function:` refs).
 */
import { err } from '../runtime/result.js';
export class FunctionRegistry {
    map = new Map();
    register(def) {
        if (this.map.has(def.name)) {
            throw new Error(`Function "${def.name}" already registered`);
        }
        // DURABLE.2 — warn loudly when a primitive omits the durability flag.
        // Default is `false` (cheap fail-safe), but silent default-false on a
        // primitive that SHOULD be durable would double-charge on every resume
        // (e.g. an llm_classify variant whose author forgot the flag). The
        // warning surfaces the omission at registration time so audit catches
        // it before it ships. `memoizable` is bundled into the same warning —
        // either both flags are explicit or neither is, by author convention.
        if (def.durable === undefined) {
            console.warn(`[opensquid:registry] Primitive "${def.name}" registered without an ` +
                `explicit \`durable\` flag — defaulting to \`false\`. Declare ` +
                `\`durable: true | false\` on the FunctionDef to silence this warning.`);
        }
        // type-erasure cast: TArgs/TResult are existentially quantified in the
        // registry's Map. Each entry knows its own schema at the value level
        // (via def.argSchema), so we don't need TS to track them per entry.
        this.map.set(def.name, def);
    }
    get(name) {
        return this.map.get(name);
    }
    has(name) {
        return this.map.has(name);
    }
    list() {
        return [...this.map.keys()].sort();
    }
    /**
     * Normalized durability metadata for a registered primitive. Returns
     * `undefined` if the name is not registered. Default-falses apply so
     * downstream code never has to repeat the `?? false` plumbing.
     */
    durability(name) {
        const def = this.map.get(name);
        if (!def)
            return undefined;
        return {
            durable: def.durable ?? false,
            memoizable: def.memoizable ?? false,
            costEstimateMs: def.costEstimateMs,
        };
    }
    async call(name, rawArgs, ctx) {
        const def = this.map.get(name);
        if (!def) {
            return err({
                kind: 'not_found',
                message: `No function "${name}"`,
            });
        }
        const parsed = def.argSchema.safeParse(rawArgs);
        if (!parsed.success) {
            return err({
                kind: 'arg_invalid',
                message: `Invalid args for "${name}": ${parsed.error.message}`,
                cause: parsed.error,
            });
        }
        return def.execute(parsed.data, ctx);
    }
}
//# sourceMappingURL=registry.js.map