import type { Subscribable } from '../observable';
import { Planner } from '../planner';
import type { Sensor } from '../sensor';
import type { Target, StrictTarget } from '../target';
import type { Task } from '../task';
import type { AgentOpts, Result } from './types';
export * from './types';
export * from './events';
/**
 * An agent is an autonomous entity that can plan and execute
 * a sequence of actions to reach a given target. It can also
 * monitor the state of the system and react to changes. It
 * can be used to implement a wide range of behaviors, from
 * simple automation to complex decision making.
 *
 * An agent internally has a knowledge database, expressed as a list
 * of hierarchical tasks, and a set of sensors that monitor the state
 * of the system. The agent uses a planner to find a sequence of
 * actions that will lead to the desired target. If found, the agent
 * will execute the plan until completion or until it's stopped. If during
 * the plan execution the state of the system changes, and task conditions
 * no longer hold true, the Agent will stop the plan execution and re-plan.
 * The same thing will happen if an error occurs while running one of the tasks
 * of the plan.
 *
 * The agent will keep re-planning until it reaches the target or until it
 * reaches the maximum number of retries. If the agent reaches the maximum
 * number of retries, it will stop and return an error.
 *
 * Plans returned by the planner are structured as a Directed Acyclic Graph
 * and as such, they tell the agent which operations can be executed in parallel,
 * and which need to be run in sequence.
 *
 * An agent is also Observable, meaning it provides a `subscribe` function that
 * receives an `Observer`. The agent will notify the observer of changes in the
 * system state, even as they happen inside long running action exections.
 *
 * Example:
 * ```ts
 * import { Agent, Task } from 'mahler';
 *
 * // Define a task to increase a counter
 * const plusOne = Task.from<number>({
 *  condition: (counter, {target}) => counter < target,
 *  effect: (counter) => ++counter._,
 *  description: '+1'
 * });
 *
 * const counter = Agent.from({
 *   // The initial system state
 *   initial: 0,
 *   // The agent knowledge database
 *   tasks: [plusOne],
 *   opts: {
 *     // Wait at minimum 10ms between re-plans
 *     // after failing to find a plan, the agent will use
 *     // exponential backoff to increase the wait time up to
 *     // a maximum given by maxWaitMs
 *     minWaitMs: 10,
 *   }
 * });
 *
 * // Tell the agent to start looking for a path to
 * // the goal. This will immediately start the agent without the
 * // need to wait. By default, the agent will continue looking for
 * // a plan forever if planning fails
 * counter.seek(10);
 *
 * // Subscribe to changes in the system state
 * counter.subscribe(console.log);
 *
 * // Wait for a result. If not given a timeout argument this may run forever
 * const res = agent.wait(1000);
 *
 * if (res.success) {
 * 	console.log(res.state); // 10
 * }
 *
 * // Stop the agent. This does not need to be awaited
 * counter.stop();
 * ```
 */
export interface Agent<TState = any> extends Subscribable<TState> {
    /**
     * Tells the agent to seek a new (relative) target.
     *
     * The method doesn't wait for a result.
     *
     * If the agent is already seeking a plan, this will cancel
     * the current execution and wait for it to be stopped
     * before starting a new run.
     *
     * @param target - The target to seek
     */
    seek(target: Target<TState>): void;
    /**
     * Tells the agent to seek a new strict target.
     *
     * A strict target means that the final state of the agent should
     * look exactly like the given target, with the exception of those properties
     * matching one of the `strictIgnore` globs in the Agent options.
     *
     * The method doesn't wait for a result.
     *
     * If the agent is already seeking a plan, this will cancel
     * the current execution and wait for it to be stopped
     * before starting a new run.
     *
     * @param target - The target to seek
     */
    seekStrict(target: StrictTarget<TState>): void;
    /**
     * Wait for the agent to reach the given target or
     * terminate due to an error.
     *
     * If the timeout is reached before the agent terminates, the
     * method will return a timeout error.
     *
     * Make sure to use a timeout if using an agent configured with `follow: true` otherwise
     * this method will wait forever.
     *
     * @timeout - The maximum time to wait for the agent to reach the target, if not provided the method will until the agent reaches a result
     */
    wait(timeout?: number): Promise<Result<TState>>;
    /**
     * Get the last known state of the agent.
     *
     * Note that if the agent is in the middle of executing an action, this
     * value may not be up to date with the actual state of the
     * system.
     */
    state(): TState;
    /**
     * Stop any running execution. This method returns
     * immediately.
     */
    stop(): void;
}
type DeepPartial<T> = T extends any[] | ((...args: any[]) => any) ? T : T extends object ? {
    [P in keyof T]?: DeepPartial<T[P]>;
} : T;
/**
 * Create a new agent
 *
 * @param config 					- The agent configuration
 * @param config.initial  - The known initial state of the system
 * @param config.tasks 		- List tasks to use for planning. If not provided, the planner must be provided
 * @param config.planner 	- Planner to use for planning. If not provided, the tasks must be provided
 * @param config.sensors 	- List of sensors to use for monitoring the state
 * @param config.opts 		- The agent runtime options
 * @param config.opts.maxRetries - The maximum number of retries before giving up
 * @param config.opts.follow - If true, the agent will keep planning until it reaches the target or until it's stopped
 * @param config.opts.maxWaitMs - The maximum time to wait between retries
 * @param config.opts.minWaitMs - The minimum time to wait between retries
 * @param config.opts.backoffMs - A function that returns the time to wait between retries. It receives the number of failures as an argument, and defaults to exponential backoff.
 * @param config.opts.logger - A Logger instance to use for logging
 */
declare function from<TState>(config: {
    initial: TState;
    planner?: Planner<TState>;
    sensors?: Array<Sensor<TState>>;
    opts?: DeepPartial<AgentOpts<TState>>;
} | {
    initial: TState;
    tasks?: Array<Task<TState, any, any>>;
    sensors?: Array<Sensor<TState>>;
    opts?: DeepPartial<AgentOpts<TState>>;
}): Agent<TState>;
export declare const Agent: {
    from: typeof from;
};
