syntax = "proto3";

import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/any.proto";

package obelisk;

service ExecutionRepository {
    rpc GenerateExecutionId(GenerateExecutionIdRequest) returns (GenerateExecutionIdResponse);
    rpc Submit(SubmitRequest) returns (SubmitResponse);
    rpc GetStatus(GetStatusRequest) returns (stream GetStatusResponse);
    rpc ListExecutions(ListExecutionsRequest) returns (ListExecutionsResponse);
    // Get the execution log
    rpc ListExecutionEvents(ListExecutionEventsRequest) returns (ListExecutionEventsResponse);
    // List child execution responses
    rpc ListResponses(ListResponsesRequest) returns (ListResponsesResponse);
    rpc ListExecutionEventsAndResponses(ListExecutionEventsAndResponsesRequest) returns (ListExecutionEventsAndResponsesResponse);
    rpc GetBacktrace(GetBacktraceRequest) returns (GetBacktraceResponse);
    rpc GetBacktraceSource(GetBacktraceSourceRequest) returns (GetBacktraceSourceResponse);
    rpc Stub(StubRequest) returns (StubResponse);
    // Cancel an activity or a cancellable workflow. Cancellation is best effort.
    rpc CancelExecution(CancelExecutionRequest) returns (CancelExecutionResponse);
    rpc CancelDelay(CancelDelayRequest) returns (CancelDelayResponse);
    rpc ListLogs(ListLogsRequest) returns (ListLogsResponse);

    // Replays the execution. An advancable (unfinished and unblocked) execution produces captured writes that can be passed to the AdvanceExecution RPC.
    // Backtraces on captured writes are display-only.
    rpc ReplayExecution(ReplayExecutionRequest) returns (ReplayExecutionResponse);
    rpc PersistExecutionBacktraces(PersistExecutionBacktracesRequest) returns (PersistExecutionBacktracesResponse);
    rpc AdvanceExecution(AdvanceExecutionRequest) returns (AdvanceExecutionResponse);
    rpc UpgradeExecutionComponent(UpgradeExecutionComponentRequest) returns (UpgradeExecutionComponentResponse);

    rpc PauseExecution(PauseExecutionRequest) returns (PauseExecutionResponse);
    rpc UnpauseExecution(UnpauseExecutionRequest) returns (UnpauseExecutionResponse);

    rpc PauseDelay(PauseDelayRequest) returns (PauseDelayResponse);
    rpc UnpauseDelay(UnpauseDelayRequest) returns (UnpauseDelayResponse);
}

service FunctionRepository {
    rpc ListComponents(ListComponentsRequest) returns (ListComponentsResponse);
    rpc GetWit(GetWitRequest) returns (GetWitResponse);
}

service DeploymentRepository {
    rpc ListDeployments(ListDeploymentsRequest) returns (ListDeploymentsResponse);
    rpc GetCurrentDeploymentId(GetCurrentDeploymentIdRequest) returns (GetCurrentDeploymentIdResponse);
    rpc SwitchDeployment(SwitchDeploymentRequest) returns (SwitchDeploymentResponse);
    rpc SubmitDeployment(SubmitDeploymentRequest) returns (SubmitDeploymentResponse);
    rpc GetDeployment(GetDeploymentRequest) returns (GetDeploymentResponse);
    // Fetch a deployment file blob by content digest.
    rpc GetFile(GetFileRequest) returns (GetFileResponse);
    // Delete content-addressed file blobs not referenced by any stored deployment
    // (e.g. orphans left by a submit that failed verification after writing blobs).
    rpc GcOrphanFiles(GcOrphanFilesRequest) returns (GcOrphanFilesResponse);
}

message GenerateExecutionIdRequest {
}

message GenerateExecutionIdResponse {
    ExecutionId execution_id = 1;
}

message ListExecutionsRequest {
    message ExecutionFunctionFilter {
        oneof scope {
            string package_name = 1;
            string interface_name = 2;
            string function_name = 3;
        }
    }
    // Filter by the current execution state, using the same buckets as `DeploymentSummary`.
    enum ExecutionStateFilter {
        EXECUTION_STATE_FILTER_UNSPECIFIED = 0;
        EXECUTION_STATE_FILTER_LOCKED = 1;
        // `PendingAt` with the scheduled time in the past.
        EXECUTION_STATE_FILTER_PENDING = 2;
        // `PendingAt` with the scheduled time in the future.
        EXECUTION_STATE_FILTER_SCHEDULED = 3;
        EXECUTION_STATE_FILTER_BLOCKED = 4;
        // Finished with any result.
        EXECUTION_STATE_FILTER_FINISHED = 5;
        // Paused regardless of the underlying state (locked, pending or blocked).
        EXECUTION_STATE_FILTER_PAUSED = 9;
        // Cancellation requested; teardown in progress.
        EXECUTION_STATE_FILTER_CANCELLING = 10;
        // Finished successfully.
        EXECUTION_STATE_FILTER_FINISHED_OK = 6;
        // Finished with the `err` variant of its result type.
        EXECUTION_STATE_FILTER_FINISHED_ERROR = 7;
        // Execution failure: trap, timeout, nondeterminism, cancellation etc.
        EXECUTION_STATE_FILTER_FINISHED_EXECUTION_FAILURE = 8;
    }
    message Cursor {
        oneof cursor {
            ExecutionId execution_id = 1;
            google.protobuf.Timestamp created_at = 2;
        }
    }
    message NewerThan {
        uint32 length = 1;
        Cursor cursor = 2;
        bool including_cursor = 3;
    }
    message OlderThan {
        uint32 length = 1;
        Cursor cursor = 2;
        bool including_cursor = 3;
    }

    optional string function_name_prefix = 1 [deprecated = true];

    // Do not return child executions
    bool top_level_only = 2;
    bool hide_finished = 5;
    optional string execution_id_prefix = 6;

    oneof pagination { // latest by `created_at` if not set
        NewerThan newer_than = 3;
        OlderThan older_than = 4;
    };

    optional ContentDigest component_digest = 7;
    optional DeploymentId deployment_id = 8;
    ExecutionFunctionFilter function_filter = 9;
    // Match executions in any of the given states (logical OR). Empty list matches all.
    repeated ExecutionStateFilter state_filters = 10;
}

message ExecutionSummary {
    ExecutionId execution_id = 1;
    FunctionName function_name = 2;
    ExecutionStatus current_status = 3;
    google.protobuf.Timestamp created_at = 4;
    google.protobuf.Timestamp first_scheduled_at = 5;
    ContentDigest component_digest = 6;
    DeploymentId deployment_id = 7;
    ComponentType component_type = 8;
}

message ListExecutionsResponse {
    repeated ExecutionSummary executions = 3;
}

message ListComponentsRequest {
    // All filters are applied if present (logical AND)
    optional FunctionName function_name = 1;
    optional ContentDigest component_digest = 2;
    bool extensions = 3;
    optional DeploymentId deployment_id = 4;
}

message ListComponentsResponse {
    repeated Component components = 1;
}

message Component {
    ComponentId component_id = 1;
    repeated FunctionDetail exports = 6; // Functions are sorted by their interface name
    repeated FunctionDetail imports = 7; // Functions are sorted by their interface name
}

enum ComponentType {
    COMPONENT_TYPE_UNSPECIFIED = 0;
    COMPONENT_TYPE_WORKFLOW = 1;
    COMPONENT_TYPE_ACTIVITY = 2;
    COMPONENT_TYPE_WEBHOOK_ENDPOINT = 3;
    COMPONENT_TYPE_ACTIVITY_STUB = 4;
    COMPONENT_TYPE_CRON = 5;
}

message FunctionDetail {
    FunctionName function_name = 1;
    repeated FunctionParameter params = 2;
    optional WitType return_type = 3;
    optional FunctionExtension extension = 4;
    bool submittable = 5;
}

enum FunctionExtension {
    FUNCTION_EXTENSION_UNSPECIFIED = 0;
    FUNCTION_EXTENSION_SUBMIT = 1;
    FUNCTION_EXTENSION_AWAIT_NEXT = 2;
    FUNCTION_EXTENSION_SCHEDULE = 3;
    FUNCTION_EXTENSION_STUB = 4;
    FUNCTION_EXTENSION_GET = 5;
}

message FunctionParameter {
    WitType type = 1;
    string name = 2;
}

message WitType {
    // WIT string as outputed by WIT printer. May contain external types
    string wit_type = 1;

    // Internal information about the type serialized as JSON
    // deprecated
    string type_wrapper = 2;

    // WIT type without external references, e.g. `variant { first, second(string) }`
    string wit_type_inline = 3;
}

message ExecutionId {
    string id = 1;
}

message RunId {
    string id = 1;
}

message JoinSetId {
    enum JoinSetKind {
        JOIN_SET_KIND_UNSPECIFIED = 0;
        JOIN_SET_KIND_ONE_OFF = 1;
        JOIN_SET_KIND_NAMED = 2;
        JOIN_SET_KIND_GENERATED = 3;
    }

    JoinSetKind kind = 2;
    string name = 3;
}

message DelayId {
    string id = 1;
}

// sha256 digest of the component
message ContentDigest {
    string digest = 1; // hex encoded with `sha256:` prefix.
}

message ComponentId {
    ComponentType component_type = 1;
    string name = 2;
    ContentDigest digest = 3;
}

message DeploymentId {
    string id = 1;
}

message FunctionName {
    // `namespace:pkg_name/ifc_name` or `namespace:pkg_name/ifc_name@version`
    string interface_name = 1;
    string function_name = 2;
}

message ComponentRetryConfig {
    optional uint32 max_retries = 1;
    google.protobuf.Duration retry_exp_backoff = 2;
}

message SubmitRequest {
    ExecutionId execution_id = 1;
    FunctionName function_name = 2;
    google.protobuf.Any params = 3;
    // If true, create the execution in paused state so it won't be picked up
    // by an executor until explicitly unpaused or advanced.
    bool paused = 4;
}

message SubmitResponse {
    enum Outcome {
        OUTCOME_UNSPECIFIED = 0;
        OUTCOME_CREATED = 1;
        OUTCOME_EXISTS_WITH_SAME_PARAMETERS = 2;
    }

    Outcome outcome = 1;
}

message StubRequest {
    ExecutionId execution_id = 1;
    google.protobuf.Any return_value = 4;
}

message StubResponse {
}

message GetStatusRequest {
    ExecutionId execution_id = 1;
    bool follow = 2;
    bool send_finished_status = 3;
}

// Corresponds to concise Pending State
message ExecutionStatus {
    message Locked {
        RunId run_id = 2;
        google.protobuf.Timestamp lock_expires_at = 3;
    }
    message PendingAt {
        google.protobuf.Timestamp scheduled_at = 1;
    }
    message BlockedByJoinSet {
        JoinSetId join_set_id = 1;
        google.protobuf.Timestamp lock_expires_at = 2;
        bool closing = 3;
    }
    message Finished {
        reserved 1;
        reserved 2;
        reserved 3;
        google.protobuf.Timestamp finished_at = 4;
        ResultKind result_kind = 5;
    }
    message Paused {
    }
    message Cancelling {
    }

    oneof status {
        Locked locked = 1;
        PendingAt pending_at = 2;
        BlockedByJoinSet blocked_by_join_set = 3;
        Finished finished = 4;
        Paused paused = 6;
        Cancelling cancelling = 7;
    }
    ContentDigest component_digest = 5;
}

message ResultKind {
    message Ok {
    }
    message Error {
    }
    oneof value {
        Ok ok = 1;
        Error error = 2;
        ExecutionFailureKind execution_failure_kind = 3;
    }
}

message FinishedStatus {
    google.protobuf.Timestamp created_at = 1;
    google.protobuf.Timestamp scheduled_at = 2;
    google.protobuf.Timestamp finished_at = 3;
    SupportedFunctionResult value = 4;
}

enum ExecutionFailureKind {
    EXECUTION_FAILURE_KIND_UNSPECIFIED = 0;
    // Applicable to activities only, because workflows will be retried forever
    EXECUTION_FAILURE_KIND_TIMED_OUT = 1;
    // Applicable to workflows
    EXECUTION_FAILURE_KIND_NONDETERMINISM_DETECTED = 2;
    // Applicable to WASM components
    EXECUTION_FAILURE_KIND_OUT_OF_FUEL = 3;
    // Applicable to activities
    EXECUTION_FAILURE_KIND_CANCELLED = 4;
    EXECUTION_FAILURE_KIND_UNCATEGORIZED = 5;
}

message SupportedFunctionResult {
    message OkPayload {
        google.protobuf.Any return_value = 1;
    }
    message ErrorPayload {
        google.protobuf.Any return_value = 1;
    }

    // FinishedExecutionFailure
    message ExecutionFailure {
        ExecutionFailureKind kind = 1;
        optional string reason = 2;
        optional string detail = 3;
    }
    oneof value {
        OkPayload ok = 1;
        ErrorPayload error = 2;
        ExecutionFailure execution_failure = 4;
    }
    optional string wit_type_inline = 5;
}

message GetStatusResponse {
    oneof message {
        // First message in the stream
        ExecutionSummary summary = 1;
        // Remaining messages
        ExecutionStatus current_status = 2;
        // Finished status, sent only if `send_finished_status` is set.
        FinishedStatus finished_status = 3;
    }
}

message ExecutionEvent {

    message Created {
        FunctionName function_name = 1;
        google.protobuf.Any params = 2;
        google.protobuf.Timestamp scheduled_at = 3;
        ComponentId component_id = 4;
        optional ExecutionId scheduled_by = 5;
        DeploymentId deployment_id = 6;
        optional ExecutionId parent_execution_id = 7;
        optional JoinSetId parent_join_set_id = 8;
        map<string, string> metadata = 9;
    }

    message Locked {
        ComponentId component_id = 1;
        DeploymentId deployment_id = 2;
        google.protobuf.Timestamp lock_expires_at = 3;
        string run_id = 4;
        string executor_id = 5;
        ComponentRetryConfig retry_config = 6;
    }

    message Unlocked {
        google.protobuf.Timestamp backoff_expires_at = 1;
        string reason = 2;
    }

    message TemporarilyFailed {
        string reason = 1;
        optional string detail = 2;
        google.protobuf.Timestamp backoff_expires_at = 3;
        repeated HttpClientTrace http_client_traces = 4;
    }

    message TemporarilyTimedOut {
        google.protobuf.Timestamp backoff_expires_at = 1;
        repeated HttpClientTrace http_client_traces = 2;
    }

    message Finished {
        SupportedFunctionResult value = 1;
        repeated HttpClientTrace http_client_traces = 2;
    }

    message Paused {
    }

    message Unpaused {
    }

    // Cancellation requested for a cancellable workflow. Sets the execution's
    // lifecycle to cancelling without changing the underlying state.
    message CancellationRequested {
    }

    message ComponentUpgradeFinished {
        message Auto {
        }
        message Manual {
            bool force = 1;
        }
        message Success {
            oneof reason {
                Auto auto = 1;
                Manual manual = 2;
            }
        }
        message Failed {
            string reason = 1;
        }
        ContentDigest component_digest = 1;
        oneof outcome {
            Success success = 2;
            Failed failed = 3;
        }
        DeploymentId deployment_id = 4;
    }

    message HistoryEvent {

        message Persist {
            message PersistKind {
                message RandomString {
                    uint64 min_length = 1;
                    uint64 max_length_exclusive = 2;
                }
                message RandomU64 {
                    uint64 min = 1;
                    uint64 max_inclusive = 2;
                }
                message ExecutionId {
                }
                oneof variant {
                    RandomString random_string = 1;
                    RandomU64 random_u64 = 2;
                    ExecutionId execution_id = 3;
                }
            }
            google.protobuf.Any data = 1;
            PersistKind kind = 2;
        }

        message JoinSetCreated {
            JoinSetId join_set_id = 1;
        }

        message JoinSetRequest {

            message DelayRequest {
                message ScheduledAt {
                    message Now {}

                    message At {
                        google.protobuf.Timestamp at = 1;
                    }

                    message In {
                        google.protobuf.Duration in = 1;
                    }

                    oneof variant {
                        Now now = 1;
                        At at = 2;
                        In in = 3;
                    }
                }
                DelayId delay_id = 1;
                google.protobuf.Timestamp expires_at = 2;
                ScheduledAt scheduled_at = 3;
                bool paused = 4;
            }

            message ChildExecutionRequest {
                message Ok {
                }
                message Error {
                    enum Kind {
                        KIND_UNSPECIFIED = 0;
                        KIND_FUNCTION_NOT_FOUND = 1;
                        KIND_TYPE_CHECK_ERROR = 2;
                    }
                    Kind kind = 1;
                    optional string detail = 2;
                }
                ExecutionId child_execution_id = 1;
                FunctionName function_name = 2;
                google.protobuf.Any params = 3;
                oneof result {
                    Ok ok = 4;
                    Error error = 5;
                }
            }

            JoinSetId join_set_id = 1;
            oneof join_set_request {
                DelayRequest delay_request = 2;
                ChildExecutionRequest child_execution_request = 3;
            }
        }

        message JoinNext {
            JoinSetId join_set_id = 1;
            google.protobuf.Timestamp run_expires_at = 2;
            bool closing = 3;
            optional FunctionName function = 4;
        }

        message JoinNextTooMany {
            JoinSetId join_set_id = 1;
            optional FunctionName function = 2;
        }

        message JoinNextTry {
            JoinSetId join_set_id = 1;

            enum Outcome {
                FOUND = 0;
                PENDING = 1;
                ALL_PROCESSED = 2;
            }
            Outcome outcome = 3;
        }

        message Schedule {
            message Ok {
            }
            message Error {
                enum Kind {
                    KIND_UNSPECIFIED = 0;
                    KIND_FUNCTION_NOT_FOUND = 1;
                    KIND_TYPE_CHECK_ERROR = 2;
                }
                Kind kind = 1;
                optional string detail = 2;
            }
            message ScheduledAt {
                message Now {}

                message At {
                    google.protobuf.Timestamp at = 1;
                }

                message In {
                    google.protobuf.Duration in = 1;
                }

                oneof variant {
                    Now now = 1;
                    At at = 2;
                    In in = 3;
                }
            }
            ExecutionId execution_id = 1;
            ScheduledAt scheduled_at = 2;
            oneof result {
                Ok ok = 3;
                Error error = 4;
            }
        }

        message Stub {
            message Ok {
            }
            message Error {
                enum Kind {
                    KIND_UNSPECIFIED = 0;
                    KIND_EXECUTION_NOT_FOUND = 1;
                    KIND_TYPE_CHECK_ERROR = 2;
                    KIND_CONFLICT = 3;
                }
                Kind kind = 1;
                optional string detail = 2;
            }
            ExecutionId execution_id = 1;
            string retval_hash = 2;
            oneof result {
                Ok ok = 3;
                Error error = 4;
            }
        }

        oneof event {
            Persist persist = 1;
            JoinSetCreated join_set_created = 2;
            JoinSetRequest join_set_request = 3;
            JoinNext join_next = 4;
            JoinNextTooMany join_next_too_many = 7;
            JoinNextTry join_next_try = 8;
            Schedule schedule = 5;
            Stub stub = 6;
        }
    }

    google.protobuf.Timestamp created_at = 1;
    uint32 version = 2;

    oneof event {
        Created created = 3;
        Locked locked = 4;
        Unlocked unlocked = 5;
        TemporarilyFailed temporarily_failed = 6;
        TemporarilyTimedOut temporarily_timed_out = 7;
        Finished finished = 8;
        HistoryEvent history_variant = 9;
        Paused paused = 11;
        Unpaused unpaused = 12;
        ComponentUpgradeFinished component_upgrade_finished = 13;
        CancellationRequested cancellation_requested = 14;
    }

    optional uint32 backtrace_id = 10;
}

message HttpClientTrace {
    google.protobuf.Timestamp sent_at = 1;
    string uri = 2;
    string method = 3;
    optional google.protobuf.Timestamp finished_at = 4;
    // present iif finished_at is sent.
    oneof result {
        uint32 status = 5;
        string error = 6;
    }
}

message ListExecutionEventsRequest {
    ExecutionId execution_id = 1;
    uint32 version_from = 2; // 0 to request the first page
    uint32 length = 3;
    bool include_backtrace_id = 4; // return version_min_including if a matching backtrace is found
}
message ListExecutionEventsResponse {
    repeated ExecutionEvent events = 2;
    uint32 max_version = 3;
}

message JoinSetResponseEvent {
    message DelayFinished {
        DelayId delay_id = 1;
        bool success = 2; // Set to `false` if delay was cancelled.
    }

    message ChildExecutionFinished {
        ExecutionId child_execution_id = 1;
        SupportedFunctionResult value = 2;
    }

    google.protobuf.Timestamp created_at = 1;
    JoinSetId join_set_id = 2;
    oneof response {
        DelayFinished delay_finished = 3;
        ChildExecutionFinished child_execution_finished = 4;
    }
}

message ResponseWithCursor {
    JoinSetResponseEvent event = 1;
    uint32 cursor = 2;
}

message ListResponsesRequest {
    ExecutionId execution_id = 1;
    uint32 cursor_from = 2; // 0 to request the first page
    uint32 length = 3;
    bool including_cursor = 4; // true to request the first page
}
message ListResponsesResponse {
    repeated ResponseWithCursor responses = 1;
    uint32 max_cursor = 2;
}

message ListExecutionEventsAndResponsesRequest {
    ExecutionId execution_id = 1;
    uint32 version_from = 2; // 0 to request the first page of execution events
    uint32 events_length = 3;
    uint32 responses_cursor_from = 4; // 0 to request the first page of responses
    uint32 responses_length = 5;
    bool responses_including_cursor = 6; // true to request the first page of responses
    bool include_backtrace_id = 7; // return version_min_including if a matching backtrace is found
}
message ListExecutionEventsAndResponsesResponse {
    repeated ExecutionEvent events = 1;
    repeated ResponseWithCursor responses = 2;
    ExecutionStatus current_status = 3;
    uint32 max_version = 4;
    uint32 max_cursor = 5;
}

message GetWitRequest {
    ContentDigest component_digest = 1;
    optional DeploymentId deployment_id = 2;
}

message GetWitResponse {
    optional string content = 1;
}

message GetBacktraceRequest {
    message First {}
    message Last {}
    message Specific {
        uint32 version = 1;
    }

    ExecutionId execution_id = 1;
    oneof filter {
        First first = 2;
        Last last = 3;
        Specific specific = 4;
    }
}

message GetBacktraceResponse {
    ComponentId component_id = 1;
    WasmBacktrace wasm_backtrace = 2;
}

message WasmBacktrace {
    repeated FrameInfo frames = 1;
    uint32 version_min_including = 2;
    uint32 version_max_excluding = 3;
}

// Display-only call-site backtrace attached to a captured write.
message CapturedBacktrace {
    ExecutionId execution_id = 1;
    ComponentId component_id = 2;
    WasmBacktrace wasm_backtrace = 3;
}

message FrameInfo {
  string module = 1;
  string func_name = 2;
  repeated FrameSymbol symbols = 3;
}

message FrameSymbol {
  optional string func_name = 1;
  optional string file = 2;
  optional uint32 line = 3;
  optional uint32 col = 4;
}

message GetBacktraceSourceRequest {
    ComponentId component_id = 1;
    string file = 2; // As appears in FrameSymbol
}

message GetBacktraceSourceResponse {
    string content = 1;
}

message CancelExecutionRequest {
    ExecutionId execution_id = 1;
}

message CancelExecutionResponse {
    enum CancelExecutionOutcome {
        CANCEL_EXECUTION_OUTCOME_UNSPECIFIED = 0;
        // Cancellation successfully requested. Activity termination is best effort; a
        // cancellable workflow moves to cancelling and is completed by the
        // cancellation driver.
        CANCEL_EXECUTION_OUTCOME_CANCELLATION_REQUESTED = 1;
        CANCEL_EXECUTION_OUTCOME_ALREADY_FINISHED = 2;
        CANCEL_EXECUTION_OUTCOME_ALREADY_CANCELLING = 3;
    }
    CancelExecutionOutcome outcome = 1;
}

message CancelDelayRequest {
    DelayId delay_id = 1;
}

message CancelDelayResponse {
    enum CancelDelayOutcome {
        CANCEL_DELAY_OUTCOME_UNSPECIFIED = 0;
        CANCEL_DELAY_OUTCOME_CANCELLED = 1;
        CANCEL_DELAY_OUTCOME_ALREADY_FINISHED = 2;
    }
    CancelDelayOutcome outcome = 1;
}

message ReplayExecutionRequest {
  ExecutionId execution_id = 1;
}

// A write captured during replay, produced by ReplayExecution and applied by AdvanceExecution.
message CapturedWrite {
  message Append {
    ExecutionId execution_id = 1;
    uint32 version = 2;
    ExecutionEvent event = 3;
    repeated CapturedBacktrace backtraces = 4;
  }
  message AppendBatch {
    reserved 1;
    repeated ExecutionEvent events = 2;
    ExecutionId execution_id = 3;
    uint32 version = 4;
    repeated CapturedBacktrace backtraces = 5;
  }
  message AppendBatchCreateNewExecution {
    reserved 1;
    repeated ExecutionEvent events = 2;
    ExecutionId execution_id = 3;
    uint32 version = 4;
    repeated CreateExecutionRequest child_requests = 5;
    repeated CapturedBacktrace backtraces = 6;
  }
  message AppendStubResponse {
    ExecutionId execution_id = 1;
    uint32 version = 2;
    repeated ExecutionEvent events = 3;
    ExecutionId parent_execution_id = 4;
    JoinSetId join_set_id = 5;
    ExecutionId child_execution_id = 6;
    SupportedFunctionResult result = 7;
    reserved 8, 9;
    uint32 finished_version = 10;
    repeated CapturedBacktrace backtraces = 11;
  }
  message AppendFinished {
    ExecutionId execution_id = 1;
    uint32 version = 2;
    ExecutionEvent.Finished event = 3;
    optional ExecutionId parent_execution_id = 4;
    optional JoinSetId parent_join_set_id = 5;
  }
  oneof write {
    Append append = 1;
    AppendBatch append_batch = 2;
    AppendBatchCreateNewExecution append_batch_create_new_execution = 3;
    AppendStubResponse append_stub_response = 4;
    AppendFinished append_finished = 5;
  }
}

// Child execution creation request (part of CapturedWrite).
message CreateExecutionRequest {
  ExecutionId execution_id = 1;
  FunctionName function_name = 2;
  google.protobuf.Any params = 3;
  google.protobuf.Timestamp scheduled_at = 4;
  ComponentId component_id = 5;
  DeploymentId deployment_id = 6;
  optional ExecutionId parent_execution_id = 7;
  optional JoinSetId parent_join_set_id = 8;
  google.protobuf.Timestamp created_at = 9;
  map<string, string> metadata = 10;
  bool paused = 11;
  optional ExecutionId scheduled_by = 12;
}

message ReplayExecutionResponse {
  message Advanceable {
    repeated CapturedWrite captured_writes = 1;
  }

  message Finished {
    SupportedFunctionResult result = 1;
  }

  message Blocked {
  }

  message ReplayFailed {
    string error = 1;
    repeated CapturedWrite captured_writes = 2;
  }

  oneof outcome {
    Advanceable advanceable = 1;
    Finished finished = 2;
    Blocked blocked = 3;
    ReplayFailed replay_failed = 4;
  }
}

message PersistExecutionBacktracesRequest {
  ExecutionId execution_id = 1;
}

message PersistExecutionBacktracesResponse {
  uint32 persisted_backtrace_count = 1;
}

message AdvanceExecutionRequest {
  ExecutionId execution_id = 1;
  repeated CapturedWrite captured_writes = 2;
  // Capture and persist fresh call-site backtraces while verifying and applying the writes.
  bool persist_backtrace = 3;
}

message AdvanceExecutionResponse {
  message Success {
    optional SupportedFunctionResult finished = 1;
  }

  message Error {
    message VersionMismatch {
      uint32 expected = 1;
    }
    message ReplayMismatch {
    }
    message TransientError {
      string message = 1;
    }

    oneof error {
      VersionMismatch version_mismatch = 3;
      ReplayMismatch replay_mismatch = 4;
      TransientError transient_error = 5;
    }
  }

  oneof result {
    Success success = 1;
    Error error = 2;
  }
}


message UpgradeExecutionComponentRequest {
  ExecutionId execution_id = 1;
  ContentDigest expected_component_digest = 2; // old
  ContentDigest new_component_digest = 3;      // new
  bool skip_determinism_check = 4;
}
message UpgradeExecutionComponentResponse {
}

// Logging

enum LogLevel {
    LOG_LEVEL_UNSPECIFIED = 0;
    LOG_LEVEL_TRACE = 1;
    LOG_LEVEL_DEBUG = 2;
    LOG_LEVEL_INFO = 3;
    LOG_LEVEL_WARN = 4;
    LOG_LEVEL_ERROR = 5;
}

enum LogStreamType {
    LOG_STREAM_TYPE_UNSPECIFIED = 0;
    LOG_STREAM_TYPE_STDOUT = 1;
    LOG_STREAM_TYPE_STDERR = 2;
}

message ListLogsRequest {
    ExecutionId execution_id = 1;
    int32 page_size = 2;
    string page_token = 3;
    bool show_logs = 4;
    bool show_streams = 5;
    repeated LogLevel levels = 6; // Only applied if show_logs = true, empty means return all levels.
    repeated LogStreamType stream_types = 7; // Only applied if show_streams = true, empty means return all stream types.
    bool show_derived = 8;
}

message ListLogsResponse {

    message LogEntry {
        google.protobuf.Timestamp created_at = 1;

        message LogVariant {
            LogLevel level = 1;
            string message = 2;
        }

        message StreamVariant {
            bytes payload = 1;
            LogStreamType stream_type = 2;
        }

        oneof entry {
            LogVariant log = 2;
            StreamVariant stream = 3;
        }

        RunId run_id = 4;
        ExecutionId execution_id = 5;
    }

    repeated LogEntry logs = 1;
    string next_page_token = 2;
    optional string prev_page_token = 3;
}

message ListDeploymentsRequest {
    message NewerThan {
        uint32 length = 1;
        optional DeploymentId cursor = 2;
        bool including_cursor = 3;
    }
    message OlderThan {
        uint32 length = 1;
        optional DeploymentId cursor = 2;
        bool including_cursor = 3;
    }

    oneof pagination { // latest if not set
        NewerThan newer_than = 1;
        OlderThan older_than = 2;
    };
    bool include_deployment_toml = 3;
    // Count child executions in the buckets too; only applies when `include_execution_counts` is set.
    bool include_derived = 4;
    // Populate `DeploymentSummary.execution_summary`.
    bool include_execution_counts = 5;
    // Populate `DeploymentSummary.component_summary`.
    bool include_component_summary = 6;
}

enum DeploymentStatus {
    DEPLOYMENT_STATUS_UNSPECIFIED = 0;
    DEPLOYMENT_STATUS_INACTIVE = 1;
    DEPLOYMENT_STATUS_ENQUEUED = 2;
    DEPLOYMENT_STATUS_ACTIVE = 3;
}

enum DeploymentComponentType {
    DEPLOYMENT_COMPONENT_TYPE_UNSPECIFIED = 0;
    DEPLOYMENT_COMPONENT_TYPE_WORKFLOW_WASM = 1;
    DEPLOYMENT_COMPONENT_TYPE_WORKFLOW_JS = 2;
    DEPLOYMENT_COMPONENT_TYPE_ACTIVITY_WASM = 3;
    DEPLOYMENT_COMPONENT_TYPE_ACTIVITY_JS = 4;
    DEPLOYMENT_COMPONENT_TYPE_ACTIVITY_EXEC = 5;
    DEPLOYMENT_COMPONENT_TYPE_ACTIVITY_STUB = 6;
    DEPLOYMENT_COMPONENT_TYPE_ACTIVITY_EXTERNAL = 7;
    DEPLOYMENT_COMPONENT_TYPE_WEBHOOK_ENDPOINT_WASM = 8;
    DEPLOYMENT_COMPONENT_TYPE_WEBHOOK_ENDPOINT_JS = 9;
    DEPLOYMENT_COMPONENT_TYPE_CRON = 10;
}

message DeploymentComponentCount {
    DeploymentComponentType component_type = 1;
    uint32 count = 2;
}

message DeploymentComponentSummary {
    repeated DeploymentComponentCount components = 1;
}

// Execution counts use disjoint buckets: a paused execution is counted only in
// `paused`, not in `locked`/`pending`/`scheduled`/`blocked`.
message DeploymentExecutionSummary {
    uint32 locked = 1;
    uint32 pending = 2;
    uint32 scheduled = 3;
    uint32 blocked = 4;
    // Paused regardless of the underlying state (locked, pending or blocked).
    uint32 paused = 5;
    // Finished successfully.
    uint32 finished_ok = 6;
    // Finished with the `err` variant of the result type.
    uint32 finished_error = 7;
    // Execution failure: trap, timeout, nondeterminism, cancellation etc.
    uint32 finished_execution_failure = 8;
    // Cancellation requested; teardown in progress.
    uint32 cancelling = 9;
}

message Deployment {
    DeploymentId deployment_id = 1;
    DeploymentStatus status = 2;
    google.protobuf.Timestamp created_at = 3;
    optional google.protobuf.Timestamp last_active_at = 4;
    // Verbatim deployment manifest (`deployment.toml`). Present when requested.
    optional string deployment_toml = 5;
    optional string description = 6;
    // Content digest = sha256(deployment_toml).
    string digest = 7;
    // Files the manifest references: (relative path, content digest).
    repeated FileRef files = 8;
}

// A deployment-owned file the manifest references, named by both its
// deployment-relative path and its content digest ("sha256:...").
message FileRef {
    string path = 1;
    string digest = 2;
    // Byte length of the referenced blob.
    uint64 size = 3;
}

message DeploymentSummary {
    reserved 2, 3, 4, 5, 6, 7, 8, 9, 10; // formerly execution count fields
    Deployment deployment = 1;
    optional DeploymentExecutionSummary execution_summary = 11;
    optional DeploymentComponentSummary component_summary = 12;
}

message ListDeploymentsResponse {
    repeated DeploymentSummary deployments = 1;
}

message GetCurrentDeploymentIdRequest {
}
message GetCurrentDeploymentIdResponse {
    DeploymentId deployment_id = 1;
}

// Policy for runtime requirements that may be unavailable on the current server,
// including environment variables, secrets, and server capabilities.
// Structural, file, compile, and link validation are always performed regardless
// of this value.
enum RuntimeConfigCheck {
    // Unset; treated as RUNTIME_CONFIG_CHECK_STRICT.
    RUNTIME_CONFIG_CHECK_UNSPECIFIED = 0;
    // Missing environment variables or secrets fail the operation.
    RUNTIME_CONFIG_CHECK_STRICT = 1;
    // Unavailable environment variables, secrets, or server capabilities are tolerated.
    RUNTIME_CONFIG_CHECK_ALLOW_UNAVAILABLE = 2;
}

message SwitchDeploymentRequest {
    DeploymentId deployment_id = 1;
    // Runtime config availability policy applied while verifying the deployment.
    // A hot redeploy rejects RUNTIME_CONFIG_CHECK_ALLOW_UNAVAILABLE.
    RuntimeConfigCheck runtime_config_check = 2;
    // Perform a hot redeployment (reload running components without restart).
    bool hot_redeploy = 3;
}
message SwitchDeploymentResponse {
    enum Outcome {
        SWITCH_OUTCOME_UNSPECIFIED = 0;
        // The server performed a hot redeploy; the new deployment is now live.
        SWITCH_OUTCOME_SWITCHED = 1;
        // The database was updated; the server must be restarted to apply the new deployment.
        SWITCH_OUTCOME_RESTART_REQUIRED = 2;
    }
    Outcome outcome = 1;
}

message SubmitDeploymentRequest {
    // Verbatim deployment manifest (`deployment.toml`), stored byte-for-byte.
    // Every deployment-owned relative `location =` must carry a `content_digest`.
    string deployment_toml = 1;
    optional string created_by = 2;
    // Runtime config availability policy applied while verifying the deployment
    // before it is persisted. Defaults to RUNTIME_CONFIG_CHECK_STRICT.
    RuntimeConfigCheck runtime_config_check = 3;
    optional string description = 4;
    // Optional client-supplied deployment ID for idempotent submission.
    // If a deployment with this ID already exists and its content digest matches
    // the submitted manifest, the submission is a no-op and returns this ID.
    // A digest mismatch is rejected. When unset, a fresh ID is generated.
    optional DeploymentId deployment_id = 5;
    // Deployment-owned file blobs referenced by the manifest. A blob may be
    // omitted when its digest already exists in the CAS; if a referenced digest
    // is neither attached here nor present in the CAS, submit fails with
    // `SubmitDeploymentErrorDetail.missing_files` and no deployment is stored.
    repeated DeploymentFileContent files = 6;
}

message DeploymentFileContent {
    // Deployment-relative path as it appears after normalizing the manifest
    // reference. Used to attach bytes to a specific manifest reference and to
    // make validation errors actionable.
    string path = 1;
    // Optional client-computed digest for clearer errors and deduplication; the
    // server always recomputes it from `content` and uses the recomputed value.
    optional string digest = 2;
    bytes content = 3;
}

message SubmitDeploymentResponse {
    DeploymentId deployment_id = 1;
}

// Structured detail attached (prost-encoded) to a FAILED_PRECONDITION /
// INVALID_ARGUMENT submit status. Each list points at the offending manifest
// reference so callers can act without guessing.
message SubmitDeploymentErrorDetail {
    // Deployment-owned references whose `content_digest` field is absent.
    repeated FileIssue missing_digest_fields = 1;
    // Referenced digests neither attached in the request nor present in CAS.
    repeated FileIssue missing_files = 2;
    // Attached blobs whose digest is not referenced by the manifest.
    repeated FileIssue unexpected_files = 3;
    // Attached blobs whose recomputed digest disagrees with the supplied one.
    repeated DigestMismatch digest_mismatches = 4;
    // Attached blobs exceeding the per-file size limit.
    repeated FileIssue oversized_files = 5;
}

message FileIssue {
    // Example: "activity_js", "workflow_wasm.backtrace.sources".
    string section = 1;
    // Component name after name derivation, when available.
    optional string component_name = 2;
    // Stable field path, e.g. "activity_js[name=send-email].location".
    string field_path = 3;
    // Deployment-relative file path, when the issue is tied to a path.
    optional string path = 4;
    // Expected digest, when known. Absent for `missing_digest_fields`.
    optional string digest = 5;
    string message = 6;
}

message DigestMismatch {
    FileIssue file = 1;
    string supplied_digest = 2;
    string actual_digest = 3;
}

message GetFileRequest {
    string digest = 1;
}
message GetFileResponse {
    bytes content = 1;
}

message GcOrphanFilesRequest {
}
message GcOrphanFilesResponse {
    // Number of orphan file blobs deleted from the content-addressed store.
    uint64 deleted_count = 1;
}

message GetDeploymentRequest {
    DeploymentId deployment_id = 1;
}
message GetDeploymentResponse {
    Deployment deployment = 1;
}


message PauseExecutionRequest {
    ExecutionId execution_id = 1;
}
message PauseExecutionResponse {
}


message UnpauseExecutionRequest {
    ExecutionId execution_id = 1;
}
message UnpauseExecutionResponse {
}

message PauseDelayRequest {
    DelayId delay_id = 1;
}
message PauseDelayResponse {
    enum PauseDelayOutcome {
        PAUSE_DELAY_OUTCOME_UNSPECIFIED = 0;
        PAUSE_DELAY_OUTCOME_PAUSED = 1;
        PAUSE_DELAY_OUTCOME_ALREADY_FINISHED = 2;
    }
    PauseDelayOutcome outcome = 1;
}

message UnpauseDelayRequest {
    DelayId delay_id = 1;
}
message UnpauseDelayResponse {
    enum UnpauseDelayOutcome {
        UNPAUSE_DELAY_OUTCOME_UNSPECIFIED = 0;
        UNPAUSE_DELAY_OUTCOME_UNPAUSED = 1;
        UNPAUSE_DELAY_OUTCOME_ALREADY_FINISHED = 2;
    }
    UnpauseDelayOutcome outcome = 1;
}
