import type { TsNode, TsRaw, TsStatements, TsImport, TsImportKind, TsImportName, TsVarDecl, TsAssign, TsFunctionDecl, TsParam, TsArrowFn, TsCall, TsAwait, TsReturn, TsObjectLiteral, TsObjectEntry, TsArrayLiteral, TsTemplateLit, TsTemplatePart, TsIf, TsElseIf, TsFor, TsWhile, TsSwitch, TsSwitchCase, TsTryCatch, TsBinOp, TsPropertyAccess, TsSpread, TsIdentifier, TsStringLiteral, TsNumericLiteral, TsBooleanLiteral, TsComment, TsExport, TsNewExpr, TsScopedVar, TsRunnerStep, TsRunnerThread, TsRunnerHandle, TsRunnerHookStep, TsRunnerIfElse, TsRunnerLoop, TsRunnerWhileLoop, TsRunnerBranchStep, TsRunnerPipe, TsEmpty, TsBreak, TsContinue, TsPostfixOp, TsTernary, TsRunnerDebugger, TsWithHandler, TsAgencyFunctionWrap } from "./tsIR.js";
export { $, TsChain } from "./fluent.js";
export declare const ts: {
    raw(code: string): TsRaw;
    statements(body: TsNode[]): TsStatements;
    statementsPush(statement: TsStatements, ...stmts: TsNode[]): TsStatements;
    importDecl(opts: {
        importKind: TsImportKind;
        names?: TsImportName[];
        defaultName?: string;
        namespaceName?: string;
        from: string;
    }): TsImport;
    varDecl(declKind: "const" | "let", name: string, initializer?: TsNode, typeAnnotation?: string): TsVarDecl;
    letDecl(name: string, initializer?: TsNode, typeAnnotation?: string): TsVarDecl;
    constDecl(name: string, initializer?: TsNode, typeAnnotation?: string): TsVarDecl;
    constDeclId(name: TsIdentifier, initializer?: TsNode, typeAnnotation?: string): TsVarDecl;
    /** `const { key: binding, ... } = initializer` — use shorthand when key === binding */
    constDestructure(bindings: Record<string, string>, initializer?: TsNode, typeAnnotation?: string): TsVarDecl;
    /** `let { key: binding, ... } = initializer` — use shorthand when key === binding */
    letDestructure(bindings: Record<string, string>, initializer?: TsNode, typeAnnotation?: string): TsVarDecl;
    assign(lhs: TsNode, rhs: TsNode): TsAssign;
    functionDecl(name: string, params: TsParam[], body: TsNode, opts?: {
        async?: boolean;
        export?: boolean;
        returnType?: string;
    }): TsFunctionDecl;
    arrowFn(params: TsParam[], body: TsNode, opts?: {
        async?: boolean;
        returnType?: string;
    }): TsArrowFn;
    call(callee: TsNode, args?: TsNode[]): TsCall;
    namedArgs(callee: TsNode, args: Record<string, TsNode>): TsCall;
    await(expr: TsNode): TsAwait;
    /**
     * `receiver.name(args)` or `receiver?.name(args)`.
     * Replaces the verbose `$(receiver).prop(name).call(args).done()` chain
     * for the common "method call on a receiver" shape.
     */
    methodCall(receiver: TsNode, name: string, args?: TsNode[], opts?: {
        optional?: boolean;
    }): TsCall;
    /** `await callee(args)` */
    awaitCall(callee: TsNode, args?: TsNode[]): TsAwait;
    /** `await receiver.name(args)` or `await receiver?.name(args)` */
    awaitMethodCall(receiver: TsNode, name: string, args?: TsNode[], opts?: {
        optional?: boolean;
    }): TsAwait;
    /**
     * Immediately-invoked (arrow) function expression: `(async () => { body })()`.
     * `body` can be a `TsStatements` body or any single `TsNode`.
     * The printer adds the wrapping parens around the arrow callee automatically.
     */
    iife(opts: {
        async?: boolean;
        params?: TsParam[];
        body: TsNode | TsNode[];
    }): TsCall;
    return(expr?: TsNode): TsReturn;
    obj(entries: TsObjectEntry[] | Record<string, TsNode>): TsObjectLiteral;
    setSpread(expr: TsNode): TsObjectEntry;
    set(key: string, value: TsNode): TsObjectEntry;
    setComputed(key: TsNode, value: TsNode): TsObjectEntry;
    arr(items: TsNode[]): TsArrayLiteral;
    template(parts: TsTemplatePart[]): TsTemplateLit;
    if(condition: TsNode, body: TsNode, opts?: {
        elseIfs?: TsElseIf[];
        elseBody?: TsNode;
    }): TsIf;
    forOf(varName: string, iterable: TsNode, body: TsNode): TsFor;
    forC(init: TsNode, condition: TsNode, update: TsNode, body: TsNode): TsFor;
    while(condition: TsNode, body: TsNode): TsWhile;
    switch(discriminant: TsNode, cases: TsSwitchCase[]): TsSwitch;
    tryCatch(tryBody: TsNode, catchBody: TsNode, catchParam?: string, finallyBody?: TsNode): TsTryCatch;
    binOp(left: TsNode, op: string, right: TsNode, opts?: {
        parenLeft?: boolean;
        parenRight?: boolean;
    }): TsBinOp;
    prop(object: TsNode, property: string, opts?: {
        optional?: boolean;
    }): TsPropertyAccess;
    index(object: TsNode, property: TsNode, opts?: {
        optional?: boolean;
    }): TsPropertyAccess;
    spread(expr: TsNode): TsSpread;
    id(name: string): TsIdentifier;
    str(value: string): TsStringLiteral;
    num(value: number): TsNumericLiteral;
    bool(value: boolean): TsBooleanLiteral;
    comment(text: string, block?: boolean): TsComment;
    export(decl?: TsNode, names?: string[]): TsExport;
    "new"(callee: TsNode, args?: TsNode[]): TsNewExpr;
    validateType(value: TsNode, zodSchema: TsNode): TsCall;
    /**
     * `await __validateChainRecursive(value, <descriptor>)` — used at
     * `!` sites whose resolved type carries at least one `@validate(...)` tag
     * anywhere in the tree. The descriptor is a TS expression built via
     * `buildValidationDescriptor(...)`. Validators read `ctx` from the
     * active `agencyStore` ALS frame, so no explicit ctx arg is threaded.
     */
    validateChainRecursive(value: TsNode, descriptor: TsNode): TsAwait;
    scopedVar(name: string, scope: TsScopedVar["scope"], moduleId?: string): TsScopedVar;
    functionReturn(value: TsNode): TsStatements;
    runnerStep(opts: {
        id: number;
        body: TsNode[];
    }): TsRunnerStep;
    runnerThread(opts: {
        id: number;
        method: "create" | "createSubthread";
        body: TsNode[];
        label?: TsNode | null;
        summarize?: TsNode | null;
        continueExpr?: TsNode | null;
        sessionExpr?: TsNode | null;
        hidden?: TsNode | null;
    }): TsRunnerThread;
    runnerHandle(opts: {
        id: number;
        handler: TsNode;
        body: TsNode[];
    }): TsRunnerHandle;
    /** Emit `await runner.hook(id, async () => { ...body... })` — a
     *  substep-counter-idempotent wrapper for codegen-emitted callback
     *  hook sites. See `Runner.hook`'s JSDoc. */
    runnerHookStep(opts: {
        id: number;
        body: TsNode[];
    }): TsRunnerHookStep;
    withHandler(handler: TsNode, body: TsNode): TsWithHandler;
    runnerIfElse(opts: {
        id: number;
        branches: {
            condition: TsNode;
            body: TsNode[];
        }[];
        elseBranch?: TsNode[];
    }): TsRunnerIfElse;
    runnerLoop(opts: {
        id: number;
        items: TsNode;
        itemVar: string;
        body: TsNode[];
        indexVar?: string;
    }): TsRunnerLoop;
    runnerWhileLoop(opts: {
        id: number;
        condition: TsNode;
        body: TsNode[];
    }): TsRunnerWhileLoop;
    runnerBranchStep(opts: {
        id: number;
        branchKey: string;
        body: TsNode[];
    }): TsRunnerBranchStep;
    runnerDebugger(opts: {
        id: number;
        label: string;
    }): TsRunnerDebugger;
    runnerPipe(opts: {
        id: number;
        target: TsNode;
        input: TsNode;
        fn: TsNode;
    }): TsRunnerPipe;
    empty(): TsEmpty;
    break(): TsBreak;
    continue(): TsContinue;
    postfix(operand: TsNode, op: "++" | "--"): TsPostfixOp;
    /** Call runner.halt(value) and return from the current callback */
    runnerHalt(value: TsNode): TsStatements;
    /** Halt with { messages: __threads(), data: value } and return from a graph node callback */
    nodeResult(value: TsNode): TsStatements;
    /**
     * Wrap a list of statements in
     * `await agencyStore.run({ ctx, stack, threads }, async () => { ... })`.
     *
     * Defense-in-depth: every function/node body's try block carries an
     * ALS frame so stdlib helpers and `__threads()` / `__stateStack()`
     * reads resolve correctly even for code that runs between Runner
     * steps. Today the gap is empty (every callback emission uses
     * `runner.step`/`runner.hook`/etc. which re-seed the frame
     * themselves), but the wrap makes the contract explicit and
     * removes the risk that a future refactor silently loses the
     * per-scope frame.
     *
     * NOTE: `return` statements inside the wrapped body escape only the
     * inner async callback, not the outer function. Validation guards
     * that return a non-`undefined` value (e.g. `return __vr_x` for a
     * validation failure) must stay outside the wrap so the outer
     * function actually returns the value to its caller. The bare
     * `return;` emitted by `runnerHalt` is fine: after the callback
     * returns, the outer `if (runner.halted) return runner.haltResult;`
     * check picks up the halted result.
     */
    withAlsFrame({ ctx, stack, threads, body, }: {
        ctx: TsNode;
        stack: TsNode;
        threads: TsNode;
        body: TsNode[];
    }): TsNode;
    env(varName: string): TsRaw;
    /**
     * Emit `await callHook({ name, data })`.
     *
     * `callHook` reads `ctx` from the active `agencyStore` frame, so the
     * codegen no longer needs to thread `__ctx` through every emission
     * site. Callback bodies cannot raise interrupts (statically forbidden
     * by the typechecker — see `checkCallbackBodyInterrupts`). `callHook`
     * returns `void`; the codegen-emitted hook sites fire-and-forget.
     */
    callHook(hookName: string, data: Record<string, TsNode> | TsNode): TsNode;
    setupEnv({ stack, step, self, ctx, }: {
        stack: TsNode;
        step: TsNode;
        self: TsNode;
        ctx: TsNode;
    }): TsStatements;
    time(varName: string): TsNode;
    stack(varName: string): TsNode;
    ctx(varName: string): TsNode;
    self(varName: string): TsNode;
    throw(code: string): TsNode;
    and(...conditions: TsNode[]): TsNode;
    or(...conditions: TsNode[]): TsNode;
    not(condition: TsNode): TsNode;
    unaryOp(op: string, operand: TsNode, opts?: {
        paren?: boolean;
    }): TsNode;
    functionCallConfig({ ctx, threads, stateStack, moduleId, scopeName, stepPath, }: {
        ctx: TsNode;
        threads?: TsNode;
        stateStack?: TsNode;
        moduleId?: TsNode;
        scopeName?: TsNode;
        stepPath?: TsNode;
    }): TsNode;
    newThreadStore(): TsNode;
    smoltalkSystemMessage(args: TsNode[]): TsNode;
    smoltalkUserMessage(args: TsNode[]): TsNode;
    smoltalkAssistantMessage(args: TsNode[]): TsNode;
    goToNode(nodeName: string, args: TsNode): TsNode;
    nodeReturn({ messages, data }: {
        messages: TsNode;
        data: TsNode;
    }): TsStatements;
    jsonStringify(value: TsNode): TsNode;
    consoleLog(...args: TsNode[]): TsNode;
    consoleWarn(...args: TsNode[]): TsNode;
    consoleError(...args: TsNode[]): TsNode;
    /** GlobalStore operations.
     *
     *  Receiver defaults to `getRuntimeContext().ctx` (strict accessor —
     *  throws clearly if no ALS frame). Pass `ctxRef` explicitly when the
     *  emission site has a different lexical context — most notably the
     *  `__initializeGlobals(__ctx)` helper body in sectionAssembler.ts,
     *  where `__ctx` is a parameter and ALS isn't yet installed; pass
     *  `ts.id("__ctx")` there. Routing through the strict accessor by
     *  default keeps the no-frame failure mode loud and aligns with the
     *  two-flavor rule in docs/dev/codegen-als-accessors.md. */
    globalGet(moduleId: string, varName: string, ctxRef?: TsNode): TsCall;
    globalSet(moduleId: string, varName: string, value: TsNode, ctxRef?: TsNode): TsCall;
    ternary(condition: TsNode, trueExpr: TsNode, falseExpr: TsNode): TsTernary;
    agencyFunctionWrap(fn: TsNode, name: string, module: string, params: {
        name: string;
    }[]): TsAgencyFunctionWrap;
    /** Predefined runtime identifiers. `threads` and `stateStack` are
     *  `__threads()` / `__stateStack()` accessor calls (not bare
     *  identifiers) because post-ALS migration the per-scope
     *  `ThreadStore` and `StateStack` live on the active `agencyStore`
     *  frame instead of in codegen-emitted `const __threads` / `const
     *  __stateStack` locals. Every site that referenced the old locals
     *  now emits the accessor call, which reads from ALS and returns
     *  the live store (or `undefined` outside any frame — see
     *  `runtime/asyncContext.ts`).
     *
     *  `ctx` is `getRuntimeContext().ctx` rather than a bare `__ctx()`
     *  accessor call: the per-scope `const __ctx = __state?.ctx ||
     *  __globalCtx;` local emitted by `setupEnv` would shadow a bare
     *  `__ctx` accessor identifier — and the esbuild TS transform
     *  rewrites *every* `__ctx` reference in a scope (import-bound
     *  ones included) to match the renamed local, so `__ctx()` would
     *  become `__ctx2()` and crash with `__ctx2 is not a function`.
     *  Routing through `getRuntimeContext().ctx` avoids the shadow
     *  entirely. Pre-wrap, parameter-context, and seed sites still use
     *  `ts.id("__ctx")` directly to reach the setupEnv local. */
    runtime: {
        self: TsIdentifier;
        ctx: TsRaw;
        threads: TsRaw;
        stateStack: TsRaw;
        stack: TsIdentifier;
        step: TsIdentifier;
        state: TsIdentifier;
        globalCtx: TsIdentifier;
        client: TsIdentifier;
    };
    /** Thread operations */
    threads: {
        create(): TsCall;
        createAndReturnThread(): TsCall;
        createSubthread(): TsCall;
        createAndReturnSubthread(): TsCall;
        get(id: TsNode): TsCall;
        active(): TsCall;
        getOrCreateActive(): TsCall;
    };
};
