package golem:api@1.3.0;

/// The Golem host API provides low level access to Golem specific features such as promises and control over
/// the durability and transactional guarantees the executor provides.
interface host {
    use wasi:clocks/monotonic-clock@0.2.3.{duration};
    use golem:rpc/types@0.2.2.{component-id, uuid, value-and-type, agent-id};
    use wasi:io/poll@0.2.3.{pollable};

    /// An index into the persistent log storing all performed operations of an agent
    type oplog-index = u64;

    /// A promise ID is a value that can be passed to an external Golem API to complete that promise
    /// from an arbitrary external source, while Golem agents can await for this completion.
    record promise-id {
        agent-id: agent-id,
        oplog-idx: oplog-index,
    }

    /// Represents a Golem component's version
    type component-revision = u64;

    /// Represents a Golem Cloud account
    record account-id {
        uuid: uuid
    }

    /// Represents a Golem environment
    record environment-id {
      uuid: uuid,
    }

    /// Configures how the executor retries failures
    record retry-policy {
        /// The maximum number of retries before the agent becomes permanently failed
        max-attempts: u32,
        /// The minimum delay between retries (applied to the first retry)
        min-delay: duration,
        /// The maximum delay between retries
        max-delay: duration,
        /// Multiplier applied to the delay on each retry to implement exponential backoff
        multiplier: f64,
        /// The maximum amount of jitter to add to the delay
        max-jitter-factor: option<f64>
    }

    /// Configurable persistence level for agents
    variant persistence-level {
        persist-nothing,
        persist-remote-side-effects,
        smart
    }

    /// Describes how to update an agent to a different component version
    enum update-mode {
        /// Automatic update tries to recover the agent using the new component version
        /// and may fail if there is a divergence.
        automatic,

        /// Manual, snapshot-based update uses a user-defined implementation of the `save-snapshot` interface
        /// to store the agent's state, and a user-defined implementation of the `load-snapshot` interface to
        /// load it into the new version.
        snapshot-based
    }

    /// Operators used in filtering enumerated agents
    enum filter-comparator {
        equal,
        not-equal,
        greater-equal,
        greater,
        less-equal,
        less
    }

    /// Operators used on strings in filtering enumerated agents
    enum string-filter-comparator {
        equal,
        not-equal,
        like,
        not-like,
        starts-with
    }

    /// The current status of an agent
    enum agent-status {
        /// The agent is running an invoked function
        running,
        /// The agent is ready to run an invoked function
        idle,
        /// An invocation is active but waiting for something (sleeping, waiting for a promise)
        suspended,
        /// The last invocation was interrupted but will be resumed
        interrupted,
        /// The last invocation failed and a retry was scheduled
        retrying,
        /// The last invocation failed and the agent can no longer be used
        failed,
        /// The agent exited after a successful invocation and can no longer be invoked
        exited,
    }

    /// Describes a filter condition on agent IDs when enumerating agents
    record agent-name-filter {
        comparator: string-filter-comparator,
        value: string
    }

    /// Describes a filter condition on the agent status when enumerating agents
    record agent-status-filter {
        comparator: filter-comparator,
        value: agent-status
    }

    /// Describes a filter condition on the component version when enumerating agents
    record agent-version-filter {
        comparator: filter-comparator,
        value: u64
    }

    /// Describes a filter condition on the agent's creation time when enumerating agents
    record agent-created-at-filter {
        comparator: filter-comparator,
        value: u64
    }

    /// Describes a filter condition on the agent's environment variables when enumerating agents
    record agent-env-filter {
        name: string,
        comparator: string-filter-comparator,
        value: string
    }

    /// Describes a filter condition on the agent's configuration variables when enumerating agents
    record agent-config-vars-filter {
        name: string,
        comparator: string-filter-comparator,
        value: string
    }

    /// Describes one filter condition for enumerating agents
    variant agent-property-filter {
        name(agent-name-filter),
        status(agent-status-filter),
        version(agent-version-filter),
        created-at(agent-created-at-filter),
        env(agent-env-filter),
        wasi-config-vars(agent-config-vars-filter)
    }

    /// Combines multiple filter conditions with an `AND` relationship for enumerating agents
    record agent-all-filter {
        filters: list<agent-property-filter>
    }

    /// Combines multiple groups of filter conditions with an `OR` relationship for enumerating agents
    record agent-any-filter {
        filters: list<agent-all-filter>
    }

    /// Metadata about an agent
    record agent-metadata {
        /// The agent ID, consists of the component ID, agent type and agent parameters
        agent-id: agent-id,
        /// Command line arguments seen by the agent
        args: list<string>,
        /// Environment variables seen by the agent
        env: list<tuple<string, string>>,
        /// Configuration variables seen by the agent
        config-vars: list<tuple<string, string>>,
        /// The current agent status
        status: agent-status,
        /// The component version the agent is running with
        component-revision: u64,
        /// The agent's current retry count
        retry-count: u64
    }

    /// Creates an agent enumeration
    resource get-agents {
        /// Creates an agent enumeration request. It is going to enumerate all agents of all the agent types
        /// defined in `component-id`, filtered by the conditions given by `filter`. If `precise` is true,
        /// the server will calculate the most recent state of all the returned agents, otherwise the returned
        /// metadata will be not guaranteed to be up-to-date.
        constructor(component-id: component-id, filter: option<agent-any-filter>, precise: bool);

        /// Retrieves the next batch of agent metadata.
        get-next: func() -> option<list<agent-metadata>>;
    }

    /// Target parameter for the `revert-agent` operation
    variant revert-agent-target {
        /// Revert to a specific oplog index. The given index will be the last one to be kept.
        revert-to-oplog-index(oplog-index),
        /// Revert the last N invocations.
        revert-last-invocations(u64)
    }

    /// Details about the fork result
    record fork-details {
        forked-phantom-id: uuid,
    }

    /// Indicates which agent the code is running on after `fork`.
    /// The parameter contains details about the fork result, such as the phantom-ID of the newly
    /// created agent.
    variant fork-result {
        /// The original agent that called `fork`
        original(fork-details),
        /// The new agent
        forked(fork-details)
    }

    resource get-promise-result {
      /// Returns a pollable that can be used to wait for the promise to become ready.j
      subscribe: func() -> pollable;
      /// Poll the result of the promise, returning none if it is not yet ready.
      get: func() -> option<list<u8>>;
    }

    /// Create a new promise
    create-promise: func() -> promise-id;

    /// Gets a handle to the result of the promise. Can only be called in the same agent that orignally created the promise.
    get-promise: func(promise-id: promise-id) -> get-promise-result;

    /// Completes the given promise with the given payload. Returns true if the promise was completed, false
    /// if the promise was already completed. The payload is passed to the agent that is awaiting the promise.
    complete-promise: func(promise-id: promise-id, data: list<u8>) -> bool;

    /// Returns the current position in the persistent op log
    get-oplog-index: func() -> oplog-index;

    /// Makes the current agent travel back in time and continue execution from the given position in the persistent
    /// op log.
    set-oplog-index: func(oplog-idx: oplog-index);

    /// Blocks the execution until the oplog has been written to at least the specified number of replicas,
    /// or the maximum number of replicas if the requested number is higher.
    oplog-commit: func(replicas: u8);

    /// Marks the beginning of an atomic operation.
    /// In case of a failure within the region selected by `mark-begin-operation` and `mark-end-operation`
    /// the whole region will be reexecuted on retry.
    /// The end of the region is when `mark-end-operation` is called with the returned oplog-index.
    mark-begin-operation: func() -> oplog-index;

    /// Commits this atomic operation. After `mark-end-operation` is called for a given index, further calls
    /// with the same parameter will do nothing.
    mark-end-operation: func(begin: oplog-index);

    /// Gets the current retry policy associated with the agent
    get-retry-policy: func() -> retry-policy;

    /// Overrides the current retry policy associated with the agent. Following this call, `get-retry-policy` will return the
    /// new retry policy.
    set-retry-policy: func(new-retry-policy: retry-policy);

    /// Gets the agent's current persistence level.
    get-oplog-persistence-level: func() -> persistence-level;

    /// Sets the agent's current persistence level. This can increase the performance of execution in cases where durable
    /// execution is not required.
    set-oplog-persistence-level: func(new-persistence-level: persistence-level);

    /// Gets the current idempotence mode. See `set-idempotence-mode` for details.
    get-idempotence-mode: func() -> bool;

    /// Sets the current idempotence mode. The default is true.
    /// True means side-effects are treated idempotent and Golem guarantees at-least-once semantics.
    /// In case of false the executor provides at-most-once semantics, failing the agent in case it is
    /// not known if the side effect was already executed.
    set-idempotence-mode: func(idempotent: bool);

    /// Generates an idempotency key. This operation will never be replayed —
    /// i.e. not only is this key generated, but it is persisted and committed, such that the key can be used in third-party systems (e.g. payment processing)
    /// to introduce idempotence.
    generate-idempotency-key: func() -> uuid;

    /// Initiates an update attempt for the given agent. The function returns immediately once the request has been processed,
    /// not waiting for the agent to get updated.
    update-agent:  func(agent-id: agent-id, target-revision: component-revision, mode: update-mode);

    /// Get the current agent's metadata
    get-self-metadata: func() -> agent-metadata;

    /// Get agent metadata
    get-agent-metadata: func(agent-id: agent-id) -> option<agent-metadata>;

    /// Fork an agent to another agent at a given oplog index
    fork-agent: func(source-agent-id: agent-id, target-agent-id: agent-id, oplog-idx-cut-off: oplog-index);

    /// Revert an agent to a previous state
    revert-agent: func(agent-id: agent-id, revert-target: revert-agent-target);

    /// Get the component-id for a given component reference.
    /// Returns none when no component with the specified reference exists.
    /// The syntax of the component reference is implementation dependent.
    ///
    /// Golem OSS: "{component_name}"
    /// Golem Cloud:
    ///     1: "{component_name}" -> will resolve in current account and project
    ///     2: "{project_name}/{component_name}" -> will resolve in current account
    ///     3: "{account_id}/{project_name}/{component_name}"
    resolve-component-id: func(component-reference: string) -> option<component-id>;

    /// Get the agent-id for a given component and agent name.
    /// Returns none when no component for the specified reference exists.
    resolve-agent-id: func(component-reference: string, agent-name: string) -> option<agent-id>;

    /// Get the agent-id for a given component and agent-name.
    /// Returns none when no component for the specified component-reference or no agent with the specified agent-name exists.
    resolve-agent-id-strict: func(component-reference: string, agent-name: string) -> option<agent-id>;

    /// Forks the current agent at the current execution point. The new agent gets the same base agent ID but
    /// with a new unique phantom ID. The phantom ID of the forked agent is returned in `fork-result` on
    /// both sides. The newly created agent continues running from the same point, but the return value is
    /// going to be different in this agent and the forked agent.
    fork: func() -> fork-result;
}

/// Interface providing user-defined snapshotting capability. This can be used to perform manual update of agents
/// when the new component incompatible with the old one.
interface save-snapshot {
    /// Saves the component's state into a user-defined snapshot
    save: func() -> list<u8>;
}

/// Interface providing user-defined snapshotting capability. This can be used to perform manual update of agents
/// when the new component incompatible with the old one.
interface load-snapshot {
    /// Tries to load a user-defined snapshot, setting up the agent's state based on it.
    /// The function can return with a failure to indicate that the update is not possible.
    load: func(bytes: list<u8>) -> result<_, string>;
}

world golem-host {
    import host;
    import save-snapshot;
    import load-snapshot;
}
