# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc

from ai.stigmer.agentic.workflowexecution.v1 import api_pb2 as ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2
from ai.stigmer.agentic.workflowexecution.v1 import io_pb2 as ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2
from ai.stigmer.commons.apiresource import io_pb2 as ai_dot_stigmer_dot_commons_dot_apiresource_dot_io__pb2


class WorkflowExecutionCommandControllerStub(object):
    """WorkflowExecutionCommandController handles write operations (Create, Update, Delete) for WorkflowExecution resources.

    This service follows the Command-Query Separation (CQS) pattern:
    - CommandController: Write operations (create, update, delete)
    - QueryController: Read operations (get, list, search)

    Authorization:
    All RPCs use custom authorization logic implemented in middleware.
    Custom authorization is needed because:
    - create: Must verify user has "execute" permission on the referenced WorkflowInstance
    - update: Only the workflow runner (system) can update execution status, not users

    Service Options:
    - api_resource_kind: workflow_execution - Links this service to the WorkflowExecution resource
    """

    def __init__(self, channel):
        """Constructor.

        Args:
            channel: A grpc.Channel.
        """
        self.create = channel.unary_unary(
                '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/create',
                request_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
                response_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
                _registered_method=True)
        self.update = channel.unary_unary(
                '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/update',
                request_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
                response_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
                _registered_method=True)
        self.updateStatus = channel.unary_unary(
                '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/updateStatus',
                request_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.WorkflowExecutionUpdateStatusInput.SerializeToString,
                response_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
                _registered_method=True)
        self.submitApproval = channel.unary_unary(
                '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/submitApproval',
                request_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.SubmitWorkflowApprovalInput.SerializeToString,
                response_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
                _registered_method=True)
        self.delete = channel.unary_unary(
                '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/delete',
                request_serializer=ai_dot_stigmer_dot_commons_dot_apiresource_dot_io__pb2.ApiResourceId.SerializeToString,
                response_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
                _registered_method=True)
        self.sendSignal = channel.unary_unary(
                '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/sendSignal',
                request_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.SendSignalInput.SerializeToString,
                response_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
                _registered_method=True)
        self.cancel = channel.unary_unary(
                '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/cancel',
                request_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.CancelWorkflowExecutionInput.SerializeToString,
                response_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
                _registered_method=True)
        self.terminate = channel.unary_unary(
                '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/terminate',
                request_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.TerminateWorkflowExecutionInput.SerializeToString,
                response_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
                _registered_method=True)
        self.recover = channel.unary_unary(
                '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/recover',
                request_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.RecoverWorkflowExecutionInput.SerializeToString,
                response_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
                _registered_method=True)
        self.pause = channel.unary_unary(
                '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/pause',
                request_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.PauseWorkflowExecutionInput.SerializeToString,
                response_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
                _registered_method=True)
        self.resume = channel.unary_unary(
                '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/resume',
                request_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.ResumeWorkflowExecutionInput.SerializeToString,
                response_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
                _registered_method=True)


class WorkflowExecutionCommandControllerServicer(object):
    """WorkflowExecutionCommandController handles write operations (Create, Update, Delete) for WorkflowExecution resources.

    This service follows the Command-Query Separation (CQS) pattern:
    - CommandController: Write operations (create, update, delete)
    - QueryController: Read operations (get, list, search)

    Authorization:
    All RPCs use custom authorization logic implemented in middleware.
    Custom authorization is needed because:
    - create: Must verify user has "execute" permission on the referenced WorkflowInstance
    - update: Only the workflow runner (system) can update execution status, not users

    Service Options:
    - api_resource_kind: workflow_execution - Links this service to the WorkflowExecution resource
    """

    def create(self, request, context):
        """Create and trigger a new workflow execution.

        This RPC creates a WorkflowExecution resource and immediately triggers it for execution.
        The workflow execution engine (Temporal) picks up the execution and begins processing tasks.

        Input Validation:
        - metadata.org must be specified
        - spec.workflow_instance_id is required and must reference an existing WorkflowInstance
        - api_version must be exactly "agentic.stigmer.ai/v1"
        - kind must be exactly "WorkflowExecution"

        Authorization:
        Custom authorization verifies:
        1. User has "execute" permission on the referenced WorkflowInstance
        2. User has access to all referenced Environments (from WorkflowInstance)
        3. User has access to all referenced Secrets (from runtime_env secret_refs)

        Execution Flow:
        1. Validate input (proto validation + business rules)
        2. Check authorization (user can execute WorkflowInstance)
        3. Create WorkflowExecution resource in database
        4. Set initial status.phase = EXECUTION_PENDING
        5. Trigger workflow in execution engine (Temporal)
        6. Return WorkflowExecution with status.phase = EXECUTION_PENDING or EXECUTION_IN_PROGRESS

        Status After Create:
        - status.phase: EXECUTION_PENDING (or EXECUTION_IN_PROGRESS if already picked up)
        - status.audit.created_at: Current timestamp
        - status.audit.created_by: Authenticated user ID
        - status.started_at: Not set yet (set when phase transitions to IN_PROGRESS)

        Use Cases:

        1. API-Triggered Execution:
        - User calls API to execute a workflow
        - Input: WorkflowExecution with spec.workflow_instance_id and spec.trigger_message
        - Output: WorkflowExecution with generated ID and PENDING status

        2. Webhook-Triggered Execution:
        - External system (Stripe, GitHub, etc.) sends webhook
        - Webhook handler creates WorkflowExecution with webhook payload in spec.trigger_message
        - spec.trigger_metadata captures webhook source, event type, timestamp

        3. Scheduled Execution:
        - Scheduler service creates WorkflowExecution at scheduled time
        - spec.trigger_metadata includes schedule ID and cron expression

        4. UI-Triggered Execution:
        - User clicks "Execute" button in web console
        - UI creates WorkflowExecution with user-provided inputs

        5. Workflow Chaining (Workflow A triggers Workflow B):
        - Workflow A completes, creates WorkflowExecution for Workflow B
        - spec.trigger_message contains output from Workflow A
        - spec.trigger_metadata includes parent workflow execution ID

        Error Cases:

        - INVALID_ARGUMENT:
        - workflow_instance_id is missing or invalid
        - org is missing
        - api_version or kind is incorrect

        - PERMISSION_DENIED:
        - User doesn't have "execute" permission on WorkflowInstance
        - User doesn't have access to referenced Environments
        - User doesn't have access to referenced Secrets

        - NOT_FOUND:
        - Referenced WorkflowInstance doesn't exist
        - Referenced Environment doesn't exist
        - Referenced Secret doesn't exist

        - FAILED_PRECONDITION:
        - WorkflowInstance is in invalid state (e.g., archived, disabled)
        - Too many concurrent executions (quota exceeded)

        Example Request:
        {
        "api_version": "agentic.stigmer.ai/v1",
        "kind": "WorkflowExecution",
        "metadata": {
        "name": "customer-onboarding-20250111-143022",
        "org": "acme"
        },
        "spec": {
        "workflow_instance_id": "wfi-customer-onboarding-prod",
        "trigger_message": "New signup: john.doe@example.com",
        "trigger_metadata": {
        "source": "api",
        "caller_id": "usr-jane-admin",
        "timestamp": "2025-01-11T14:30:22Z"
        },
        "runtime_env": {
        "CUSTOMER_EMAIL": { "value": "john.doe@example.com" }
        }
        }
        }

        Example Response:
        {
        "api_version": "agentic.stigmer.ai/v1",
        "kind": "WorkflowExecution",
        "metadata": {
        "id": "wfx-abc123xyz456",  // Auto-generated
        "name": "customer-onboarding-20250111-143022",
        "org": "acme"
        },
        "spec": { ... },  // Same as request
        "status": {
        "phase": 1,  // EXECUTION_PENDING
        "audit": {
        "created_at": "2025-01-11T14:30:22Z",
        "created_by": "usr-jane-admin"
        }
        }
        }
        """
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')

    def update(self, request, context):
        """Update execution with full state.
        Used by users to update execution configuration (spec fields).
        No individual field updates - always provide complete state.
        """
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')

    def updateStatus(self, request, context):
        """Update execution status during workflow execution.
        Used by workflow-runner to send progressive status updates (messages, task states, phase, etc.)
        This RPC is optimized for frequent status updates and merges status fields with existing state.

        This RPC is used by the workflow execution engine (Temporal) to update the status
        of a running workflow execution. Users cannot call this RPC directly.

        What Can Be Updated:
        - status.phase (PENDING → IN_PROGRESS → COMPLETED/FAILED/CANCELLED)
        - status.tasks (update task statuses, outputs, errors)
        - status.output (set final workflow output when COMPLETED)
        - status.error (set error message when FAILED)
        - status.started_at (set when execution starts)
        - status.completed_at (set when execution finishes)

        What Cannot Be Updated:
        - spec.* (user inputs are immutable after creation)
        - metadata.id (resource ID is immutable)
        - status.audit.created_at (creation timestamp is immutable)

        Authorization:
        Custom authorization verifies:
        1. Caller is the workflow runner service (system identity, not a user)
        2. Only status fields are being modified (spec and metadata unchanged)

        Update Flow:
        1. Workflow runner executes a task
        2. Task completes/fails
        3. Workflow runner calls updateStatus() with:
        - Updated status.tasks (new task status, output/error)
        - Updated status.phase (if all tasks done)
        4. Backend validates and persists update
        5. Backend broadcasts update via WebSocket (for real-time UI updates)

        Use Cases:

        1. Task Started:
        - Workflow runner updates status.tasks[i].status = IN_PROGRESS

        2. Task Completed:
        - Workflow runner updates status.tasks[i].status = COMPLETED
        - Workflow runner sets status.tasks[i].output

        3. Task Failed:
        - Workflow runner updates status.tasks[i].status = FAILED
        - Workflow runner sets status.tasks[i].error
        - Workflow runner updates status.phase = EXECUTION_FAILED
        - Workflow runner sets status.error
        - Workflow runner sets status.completed_at

        4. Workflow Completed:
        - Workflow runner updates status.phase = EXECUTION_COMPLETED
        - Workflow runner sets status.output
        - Workflow runner sets status.completed_at

        5. Workflow Cancelled:
        - Workflow runner receives cancellation signal
        - Workflow runner updates status.phase = EXECUTION_CANCELLED
        - Workflow runner sets status.completed_at

        Error Cases:

        - PERMISSION_DENIED:
        - Caller is not the workflow runner service (users cannot update status)

        - INVALID_ARGUMENT:
        - Trying to modify spec or metadata (only status can be updated)
        - Invalid phase transition (e.g., COMPLETED → IN_PROGRESS)

        - NOT_FOUND:
        - WorkflowExecution with given ID doesn't exist

        Example Request (Task Completed):
        {
        "metadata": {
        "id": "wfx-abc123xyz456"
        },
        "status": {
        "phase": 2,  // EXECUTION_IN_PROGRESS
        "completed_tasks": 1,
        "tasks": [
        {
        "task_id": "task-1",
        "task_name": "validate_email",
        "status": 3,  // WORKFLOW_TASK_COMPLETED
        "output": { "valid": true },
        "completed_at": "2025-01-11T14:30:27Z"
        },
        {
        "task_id": "task-2",
        "task_name": "create_account",
        "status": 2,  // WORKFLOW_TASK_IN_PROGRESS
        "timestamp": "2025-01-11T14:30:27Z"
        }
        ]
        }
        }
        """
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')

    def submitApproval(self, request, context):
        """Submit approval for a child agent's tool execution (HITL Phase 5.3).

        This RPC forwards the approval decision to the child AgentExecution that
        is waiting for approval. The child is identified by the child_agent_execution_id
        in status.pending_approval.

        ## Behavior

        When a workflow invokes an agent that requires tool approval, the approval
        request surfaces at the workflow level via status.pending_approval. Users can
        submit their decision through this RPC, which forwards it to the child agent.

        The approval is forwarded to the child via AgentExecution.submitApproval RPC,
        ensuring consistent validation and Temporal workflow signaling.

        ## Preconditions

        - status.pending_approval must be populated
        - tool_call_id must match status.pending_approval.tool_call_id
        - status.pending_approval.child_agent_execution_id must not be empty
        - User must have can_edit permission on the workflow execution

        ## State Transitions

        After successful approval:
        - Approval is forwarded to child AgentExecution
        - Child agent resumes execution based on action (APPROVE/SKIP/REJECT)
        - Child agent clears its pending_approval, which triggers signal to parent
        - WorkflowExecution.status.pending_approval is eventually cleared
        - Workflow task status returns from WAITING_APPROVAL to IN_PROGRESS

        ## Approval Actions

        - APPROVE: Tool executes with the provided arguments
        - SKIP: Tool execution is skipped, agent continues with skip message
        - REJECT: Agent execution fails with rejection error

        ## Error Cases

        - NOT_FOUND: Workflow execution doesn't exist
        - PERMISSION_DENIED: User doesn't have can_edit permission
        - FAILED_PRECONDITION: No pending approval, or child agent not waiting
        - INVALID_ARGUMENT: Tool call ID mismatch, or action is UNSPECIFIED
        - UNAVAILABLE: Failed to forward to child agent (transient error)

        ## Idempotency

        If the same approval is submitted twice (same workflow execution, tool_call_id,
        and action), the second call is a no-op if the approval was already processed.

        ## Alternative: Direct Agent Approval

        Users can also submit approvals directly via AgentExecution.submitApproval
        using the child_agent_execution_id. Both paths are equivalent and result
        in the same state transitions.

        @since Phase 5.3 (Approval Forwarding)
        """
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')

    def delete(self, request, context):
        """Delete an execution.
        """
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')

    def sendSignal(self, request, context):
        """─────────────────────────────────────────────────────────────────────────────
        Signal Operations

        These RPCs enable communication with running workflow executions via
        Temporal signals. Signals are used to deliver external events to workflows
        waiting at LISTEN tasks.
        ─────────────────────────────────────────────────────────────────────────────

        Send a signal to a running workflow execution.

        Delivers a signal to a workflow execution, typically to unblock a LISTEN task.
        Uses Temporal's SignalWithStart API internally for race-proof delivery.

        ## Behavior

        1. Validates execution exists and is in a signalable phase
        2. Uses Temporal SignalWithStart for atomic delivery:
        - If workflow exists → sends signal immediately
        - If workflow not started yet → starts workflow, then sends signal
        3. Signal is delivered to workflow's signal channel
        4. LISTEN task waiting for this signal will unblock and continue
        5. Returns the current WorkflowExecution state

        ## Preconditions

        - Execution must be in EXECUTION_PENDING or EXECUTION_IN_PROGRESS phase
        - Cannot signal terminal executions (COMPLETED, FAILED, CANCELLED, TERMINATED)
        - User must have can_edit permission on the workflow execution

        ## Race-Proof Delivery (SignalWithStart)

        This RPC uses Temporal's SignalWithStart API to handle the race condition
        where a signal might arrive before the workflow is fully started:
        - Traditional SignalWorkflow fails with "WorkflowNotFound" if called too early
        - SignalWithStart atomically: starts workflow if needed, then sends signal
        - Guarantees signal delivery even in race conditions

        ## Signal Matching

        The signal_name must match the signal ID defined in the workflow's LISTEN task:

        Workflow YAML:
        - waitForPayment:
        listen:
        to:
        one:
        with:
        id: payment_confirmed  # <-- signal_name must match this
        type: signal

        API Call:
        { "signal_name": "payment_confirmed", "payload": {...} }

        ## Error Cases

        - NOT_FOUND: Workflow execution doesn't exist
        - PERMISSION_DENIED: User doesn't have can_edit permission
        - FAILED_PRECONDITION: Execution is in a terminal phase
        - INVALID_ARGUMENT: execution_id or signal_name is empty

        ## Example Request

        {
        "execution_id": "wfx-abc123xyz456",
        "signal_name": "payment_confirmed",
        "payload": {
        "transaction_id": "txn_123",
        "amount": 99.99,
        "currency": "USD"
        }
        }

        ## Example Response

        Returns the current WorkflowExecution state (phase may still be IN_PROGRESS
        as the workflow continues after receiving the signal).

        @since Gap B1 (Signal-With-Start for race-proof event delivery)
        """
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')

    def cancel(self, request, context):
        """─────────────────────────────────────────────────────────────────────────────
        Lifecycle Control Operations

        These RPCs control the workflow execution lifecycle, mapping to Temporal
        workflow operations. They enable users to gracefully stop, force-stop,
        or recover workflow executions.
        ─────────────────────────────────────────────────────────────────────────────

        Cancel a running workflow execution gracefully.

        Sends a cancellation signal to the workflow via Temporal's CancelWorkflow API.
        The workflow code can handle the cancellation signal to perform cleanup
        (e.g., compensation logic, resource cleanup, notifications) before
        transitioning to the CANCELLED phase.

        Temporal Equivalent: `temporal workflow cancel --workflow-id <id>`

        ## Behavior

        1. Validates execution exists and is in a cancellable phase
        2. Sends cancellation signal to Temporal workflow
        3. Workflow code receives the signal and can perform cleanup
        4. Execution transitions to EXECUTION_CANCELLED phase
        5. Returns updated WorkflowExecution with new phase

        ## Preconditions

        - Execution must be in EXECUTION_PENDING or EXECUTION_IN_PROGRESS phase
        - Cannot cancel already-terminal executions (COMPLETED, FAILED, CANCELLED, TERMINATED)

        ## State Transitions

        - status.phase: PENDING/IN_PROGRESS → CANCELLED
        - status.completed_at: Set to current timestamp
        - In-progress tasks: May complete cleanup or be interrupted

        ## Idempotency

        Cancelling an already-cancelled execution succeeds as a no-op.
        The call returns the current execution state without side effects.
        This ensures safe retry of cancel requests.

        ## Error Cases

        - NOT_FOUND: Execution with given ID doesn't exist
        - PERMISSION_DENIED: User lacks can_edit permission on the execution
        - FAILED_PRECONDITION: Execution is in a terminal phase (COMPLETED, FAILED, TERMINATED)
        - INVALID_ARGUMENT: ID is empty or malformed

        ## Example Request

        {
        "id": "wfx-abc123xyz456",
        "reason": "Customer requested cancellation - order no longer needed"
        }

        ## Example Response

        {
        "api_version": "agentic.stigmer.ai/v1",
        "kind": "WorkflowExecution",
        "metadata": { "id": "wfx-abc123xyz456" },
        "status": {
        "phase": 5,  // EXECUTION_CANCELLED
        "completed_at": "2026-02-07T18:30:00Z"
        }
        }
        """
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')

    def terminate(self, request, context):
        """Terminate a workflow execution immediately.

        Force-stops the workflow via Temporal's TerminateWorkflow API without
        allowing cleanup. Unlike cancel, the workflow code cannot respond to
        termination - it is stopped immediately. Use this for stuck or
        unresponsive workflows that don't respond to cancellation.

        Temporal Equivalent: `temporal workflow terminate --workflow-id <id>`

        ## Behavior

        1. Validates execution exists and is in a terminable phase
        2. Force-kills workflow via Temporal (no signal sent to workflow)
        3. Execution transitions to EXECUTION_TERMINATED phase immediately
        4. No cleanup callbacks or defer blocks are executed
        5. Returns updated WorkflowExecution with TERMINATED phase

        ## Preconditions

        - Execution must be in EXECUTION_PENDING or EXECUTION_IN_PROGRESS phase
        - Cannot terminate already-terminal executions

        ## State Transitions

        - status.phase: PENDING/IN_PROGRESS → TERMINATED
        - status.completed_at: Set to current timestamp
        - status.error: May contain termination reason
        - In-progress tasks: Stopped abruptly (no cleanup)

        ## Terminated vs Cancelled

        | Aspect | cancel | terminate |
        |--------|--------|-----------|
        | Signal to workflow | Yes (can handle) | No |
        | Cleanup opportunity | Yes | No |
        | Use case | Normal stop | Stuck workflows |
        | Can recover? | No | No |

        ## Idempotency

        Terminating an already-terminated execution succeeds as a no-op.
        The call returns the current execution state without side effects.

        ## Error Cases

        - NOT_FOUND: Execution with given ID doesn't exist
        - PERMISSION_DENIED: User lacks can_edit permission on the execution
        - FAILED_PRECONDITION: Execution is in a terminal phase
        - INVALID_ARGUMENT: ID is empty or malformed

        ## Example Request

        {
        "id": "wfx-abc123xyz456",
        "reason": "Workflow stuck for 2 hours, not responding to cancel"
        }

        ## Example Response

        {
        "api_version": "agentic.stigmer.ai/v1",
        "kind": "WorkflowExecution",
        "metadata": { "id": "wfx-abc123xyz456" },
        "status": {
        "phase": 6,  // EXECUTION_TERMINATED
        "completed_at": "2026-02-07T18:35:00Z",
        "error": "Terminated by operator: Workflow stuck for 2 hours"
        }
        }
        """
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')

    def recover(self, request, context):
        """Recover a failed workflow execution from the last checkpoint.

        Resumes execution from the last successful point using Temporal's
        ResetWorkflow API. Completed work is preserved - successful tasks
        are NOT re-executed. This enables "retry and resume" semantics
        without duplicating side effects.

        Temporal Equivalent: `temporal workflow reset --workflow-id <id> --type LastWorkflowTask`

        ## Behavior

        1. Validates execution is in FAILED phase (recoverable)
        2. Identifies the last successful checkpoint in workflow history
        3. Creates new Temporal run from that checkpoint via ResetWorkflow
        4. Execution transitions from FAILED to IN_PROGRESS phase
        5. Workflow continues from where it failed
        6. Returns updated WorkflowExecution with IN_PROGRESS phase

        ## Preconditions

        - Execution must be in EXECUTION_FAILED phase
        - TERMINATED executions cannot be recovered (intentional hard stop)
        - CANCELLED executions cannot be recovered (intentional user action)
        - COMPLETED executions don't need recovery

        ## State Transitions

        - status.phase: FAILED → IN_PROGRESS
        - status.completed_at: Cleared (execution is running again)
        - status.error: Cleared (no longer failed)
        - Completed tasks: Preserved (not re-executed)
        - Failed tasks: Reset to pending, will be retried

        ## Recovery vs Restart

        | Aspect | recover | Create new execution |
        |--------|---------|----------------------|
        | Completed work | Preserved | Lost (re-executed) |
        | Side effects | Not duplicated | May duplicate |
        | Execution ID | Same | New ID |
        | Use case | Resume after fix | Start fresh |

        ## Idempotency

        If recovery already succeeded (execution is now IN_PROGRESS from a
        previous recover call), the call succeeds as a no-op and returns
        the current execution state.

        ## Error Cases

        - NOT_FOUND: Execution with given ID doesn't exist
        - PERMISSION_DENIED: User lacks can_edit permission on the execution
        - FAILED_PRECONDITION:
        - Execution is not in FAILED phase
        - Execution is TERMINATED (cannot recover)
        - Execution is CANCELLED (cannot recover)
        - INVALID_ARGUMENT: ID is empty or malformed

        ## Example Request

        {
        "id": "wfx-abc123xyz456",
        "reason": "Stripe API recovered, resuming payment processing"
        }

        ## Example Response

        {
        "api_version": "agentic.stigmer.ai/v1",
        "kind": "WorkflowExecution",
        "metadata": { "id": "wfx-abc123xyz456" },
        "status": {
        "phase": 2,  // EXECUTION_IN_PROGRESS
        "started_at": "2026-02-07T10:00:00Z",
        "tasks": [
        { "task_id": "task-1", "status": 3 },  // COMPLETED (preserved)
        { "task_id": "task-2", "status": 2 }   // IN_PROGRESS (resumed)
        ]
        }
        }
        """
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')

    def pause(self, request, context):
        """Pause a running workflow execution.

        Temporarily stops the workflow at its current checkpoint. Unlike cancel,
        the execution is NOT terminal and can be resumed later from where it left off.
        The workflow gracefully checkpoints and exits, preserving all progress.

        ## Behavior

        1. Validates execution exists and is in a pausable phase
        2. Sends "pause" signal to Temporal workflow
        3. Workflow receives signal and sets pauseRequested flag
        4. Running activities are gracefully cancelled (checkpoints saved)
        5. Execution transitions to EXECUTION_PAUSED phase
        6. Workflow waits for resume signal (no resources consumed)
        7. Returns updated WorkflowExecution with PAUSED phase

        ## Preconditions

        - Execution must be in EXECUTION_PENDING or EXECUTION_IN_PROGRESS phase
        - Cannot pause already-terminal executions (COMPLETED, FAILED, CANCELLED, TERMINATED)

        ## State Transitions

        - status.phase: PENDING/IN_PROGRESS → PAUSED
        - status.completed_at: NOT set (execution is not finished)
        - Running activities: Gracefully cancelled, checkpoint saved
        - LangGraph state: Preserved via thread_id checkpoint

        ## Paused vs Cancelled

        | Aspect | pause | cancel |
        |--------|-------|--------|
        | Terminal state? | No | Yes |
        | Can resume? | Yes (via resume RPC) | No |
        | Checkpoint saved? | Yes | Best-effort |
        | Progress preserved? | Yes | No |
        | Use case | Temporary stop, maintenance | Permanent stop |

        ## Agent Activity Behavior

        When pause is signaled to a workflow running an agent:
        1. Workflow cancels the running activity gracefully
        2. Python activity catches CancelledError
        3. LangGraph saves final checkpoint automatically
        4. Activity returns with paused status
        5. On resume, activity loads from checkpoint and continues

        ## Idempotency

        Pausing an already-paused execution succeeds as a no-op.
        The call returns the current execution state without side effects.

        ## Error Cases

        - NOT_FOUND: Execution with given ID doesn't exist
        - PERMISSION_DENIED: User lacks can_edit permission on the execution
        - FAILED_PRECONDITION: Execution is in a terminal phase
        - INVALID_ARGUMENT: ID is empty or malformed

        ## Example Request

        {
        "id": "wfx-abc123xyz456",
        "reason": "Pausing for scheduled maintenance window"
        }

        ## Example Response

        {
        "api_version": "agentic.stigmer.ai/v1",
        "kind": "WorkflowExecution",
        "metadata": { "id": "wfx-abc123xyz456" },
        "status": {
        "phase": 7,  // EXECUTION_PAUSED
        "started_at": "2026-02-07T10:00:00Z"
        // Note: completed_at is NOT set (execution can be resumed)
        }
        }

        @since Gap A3 (Pause/Resume Propagation)
        """
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')

    def resume(self, request, context):
        """Resume a paused workflow execution.

        Continues execution from the checkpoint where it was paused. The workflow
        re-invokes activities with the same thread_id, which loads from checkpoint
        and continues from where it left off.

        ## Behavior

        1. Validates execution is in EXECUTION_PAUSED phase
        2. Sends "resume" signal to Temporal workflow
        3. Workflow receives signal and sets resumeSignalReceived flag
        4. Workflow re-invokes activity with same execution context
        5. Activity detects resume and loads from LangGraph checkpoint
        6. Execution transitions back to EXECUTION_IN_PROGRESS phase
        7. Returns updated WorkflowExecution with IN_PROGRESS phase

        ## Preconditions

        - Execution must be in EXECUTION_PAUSED phase
        - Cannot resume non-paused executions

        ## State Transitions

        - status.phase: PAUSED → IN_PROGRESS
        - Activities: Re-invoked, load from checkpoint
        - LangGraph state: Loaded from checkpoint via thread_id

        ## Resume Behavior

        When resume is signaled to a paused workflow:
        1. Java workflow unblocks from Workflow.await()
        2. Workflow re-invokes the activity with same parameters
        3. Python activity reads thread_id from heartbeat_details
        4. LangGraph loads checkpoint using thread_id
        5. Agent continues from exact position where it was paused

        ## Idempotency

        Resuming an already-running execution succeeds as a no-op.
        The call returns the current execution state without side effects.

        ## Error Cases

        - NOT_FOUND: Execution with given ID doesn't exist
        - PERMISSION_DENIED: User lacks can_edit permission on the execution
        - FAILED_PRECONDITION: Execution is not in PAUSED phase
        - INVALID_ARGUMENT: ID is empty or malformed

        ## Example Request

        {
        "id": "wfx-abc123xyz456"
        }

        ## Example Response

        {
        "api_version": "agentic.stigmer.ai/v1",
        "kind": "WorkflowExecution",
        "metadata": { "id": "wfx-abc123xyz456" },
        "status": {
        "phase": 2,  // EXECUTION_IN_PROGRESS
        "started_at": "2026-02-07T10:00:00Z"
        }
        }

        @since Gap A3 (Pause/Resume Propagation)
        """
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')


def add_WorkflowExecutionCommandControllerServicer_to_server(servicer, server):
    rpc_method_handlers = {
            'create': grpc.unary_unary_rpc_method_handler(
                    servicer.create,
                    request_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
                    response_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
            ),
            'update': grpc.unary_unary_rpc_method_handler(
                    servicer.update,
                    request_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
                    response_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
            ),
            'updateStatus': grpc.unary_unary_rpc_method_handler(
                    servicer.updateStatus,
                    request_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.WorkflowExecutionUpdateStatusInput.FromString,
                    response_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
            ),
            'submitApproval': grpc.unary_unary_rpc_method_handler(
                    servicer.submitApproval,
                    request_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.SubmitWorkflowApprovalInput.FromString,
                    response_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
            ),
            'delete': grpc.unary_unary_rpc_method_handler(
                    servicer.delete,
                    request_deserializer=ai_dot_stigmer_dot_commons_dot_apiresource_dot_io__pb2.ApiResourceId.FromString,
                    response_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
            ),
            'sendSignal': grpc.unary_unary_rpc_method_handler(
                    servicer.sendSignal,
                    request_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.SendSignalInput.FromString,
                    response_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
            ),
            'cancel': grpc.unary_unary_rpc_method_handler(
                    servicer.cancel,
                    request_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.CancelWorkflowExecutionInput.FromString,
                    response_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
            ),
            'terminate': grpc.unary_unary_rpc_method_handler(
                    servicer.terminate,
                    request_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.TerminateWorkflowExecutionInput.FromString,
                    response_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
            ),
            'recover': grpc.unary_unary_rpc_method_handler(
                    servicer.recover,
                    request_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.RecoverWorkflowExecutionInput.FromString,
                    response_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
            ),
            'pause': grpc.unary_unary_rpc_method_handler(
                    servicer.pause,
                    request_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.PauseWorkflowExecutionInput.FromString,
                    response_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
            ),
            'resume': grpc.unary_unary_rpc_method_handler(
                    servicer.resume,
                    request_deserializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.ResumeWorkflowExecutionInput.FromString,
                    response_serializer=ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
            ),
    }
    generic_handler = grpc.method_handlers_generic_handler(
            'ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController', rpc_method_handlers)
    server.add_generic_rpc_handlers((generic_handler,))
    server.add_registered_method_handlers('ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController', rpc_method_handlers)


 # This class is part of an EXPERIMENTAL API.
class WorkflowExecutionCommandController(object):
    """WorkflowExecutionCommandController handles write operations (Create, Update, Delete) for WorkflowExecution resources.

    This service follows the Command-Query Separation (CQS) pattern:
    - CommandController: Write operations (create, update, delete)
    - QueryController: Read operations (get, list, search)

    Authorization:
    All RPCs use custom authorization logic implemented in middleware.
    Custom authorization is needed because:
    - create: Must verify user has "execute" permission on the referenced WorkflowInstance
    - update: Only the workflow runner (system) can update execution status, not users

    Service Options:
    - api_resource_kind: workflow_execution - Links this service to the WorkflowExecution resource
    """

    @staticmethod
    def create(request,
            target,
            options=(),
            channel_credentials=None,
            call_credentials=None,
            insecure=False,
            compression=None,
            wait_for_ready=None,
            timeout=None,
            metadata=None):
        return grpc.experimental.unary_unary(
            request,
            target,
            '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/create',
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
            options,
            channel_credentials,
            insecure,
            call_credentials,
            compression,
            wait_for_ready,
            timeout,
            metadata,
            _registered_method=True)

    @staticmethod
    def update(request,
            target,
            options=(),
            channel_credentials=None,
            call_credentials=None,
            insecure=False,
            compression=None,
            wait_for_ready=None,
            timeout=None,
            metadata=None):
        return grpc.experimental.unary_unary(
            request,
            target,
            '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/update',
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.SerializeToString,
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
            options,
            channel_credentials,
            insecure,
            call_credentials,
            compression,
            wait_for_ready,
            timeout,
            metadata,
            _registered_method=True)

    @staticmethod
    def updateStatus(request,
            target,
            options=(),
            channel_credentials=None,
            call_credentials=None,
            insecure=False,
            compression=None,
            wait_for_ready=None,
            timeout=None,
            metadata=None):
        return grpc.experimental.unary_unary(
            request,
            target,
            '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/updateStatus',
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.WorkflowExecutionUpdateStatusInput.SerializeToString,
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
            options,
            channel_credentials,
            insecure,
            call_credentials,
            compression,
            wait_for_ready,
            timeout,
            metadata,
            _registered_method=True)

    @staticmethod
    def submitApproval(request,
            target,
            options=(),
            channel_credentials=None,
            call_credentials=None,
            insecure=False,
            compression=None,
            wait_for_ready=None,
            timeout=None,
            metadata=None):
        return grpc.experimental.unary_unary(
            request,
            target,
            '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/submitApproval',
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.SubmitWorkflowApprovalInput.SerializeToString,
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
            options,
            channel_credentials,
            insecure,
            call_credentials,
            compression,
            wait_for_ready,
            timeout,
            metadata,
            _registered_method=True)

    @staticmethod
    def delete(request,
            target,
            options=(),
            channel_credentials=None,
            call_credentials=None,
            insecure=False,
            compression=None,
            wait_for_ready=None,
            timeout=None,
            metadata=None):
        return grpc.experimental.unary_unary(
            request,
            target,
            '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/delete',
            ai_dot_stigmer_dot_commons_dot_apiresource_dot_io__pb2.ApiResourceId.SerializeToString,
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
            options,
            channel_credentials,
            insecure,
            call_credentials,
            compression,
            wait_for_ready,
            timeout,
            metadata,
            _registered_method=True)

    @staticmethod
    def sendSignal(request,
            target,
            options=(),
            channel_credentials=None,
            call_credentials=None,
            insecure=False,
            compression=None,
            wait_for_ready=None,
            timeout=None,
            metadata=None):
        return grpc.experimental.unary_unary(
            request,
            target,
            '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/sendSignal',
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.SendSignalInput.SerializeToString,
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
            options,
            channel_credentials,
            insecure,
            call_credentials,
            compression,
            wait_for_ready,
            timeout,
            metadata,
            _registered_method=True)

    @staticmethod
    def cancel(request,
            target,
            options=(),
            channel_credentials=None,
            call_credentials=None,
            insecure=False,
            compression=None,
            wait_for_ready=None,
            timeout=None,
            metadata=None):
        return grpc.experimental.unary_unary(
            request,
            target,
            '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/cancel',
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.CancelWorkflowExecutionInput.SerializeToString,
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
            options,
            channel_credentials,
            insecure,
            call_credentials,
            compression,
            wait_for_ready,
            timeout,
            metadata,
            _registered_method=True)

    @staticmethod
    def terminate(request,
            target,
            options=(),
            channel_credentials=None,
            call_credentials=None,
            insecure=False,
            compression=None,
            wait_for_ready=None,
            timeout=None,
            metadata=None):
        return grpc.experimental.unary_unary(
            request,
            target,
            '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/terminate',
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.TerminateWorkflowExecutionInput.SerializeToString,
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
            options,
            channel_credentials,
            insecure,
            call_credentials,
            compression,
            wait_for_ready,
            timeout,
            metadata,
            _registered_method=True)

    @staticmethod
    def recover(request,
            target,
            options=(),
            channel_credentials=None,
            call_credentials=None,
            insecure=False,
            compression=None,
            wait_for_ready=None,
            timeout=None,
            metadata=None):
        return grpc.experimental.unary_unary(
            request,
            target,
            '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/recover',
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.RecoverWorkflowExecutionInput.SerializeToString,
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
            options,
            channel_credentials,
            insecure,
            call_credentials,
            compression,
            wait_for_ready,
            timeout,
            metadata,
            _registered_method=True)

    @staticmethod
    def pause(request,
            target,
            options=(),
            channel_credentials=None,
            call_credentials=None,
            insecure=False,
            compression=None,
            wait_for_ready=None,
            timeout=None,
            metadata=None):
        return grpc.experimental.unary_unary(
            request,
            target,
            '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/pause',
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.PauseWorkflowExecutionInput.SerializeToString,
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
            options,
            channel_credentials,
            insecure,
            call_credentials,
            compression,
            wait_for_ready,
            timeout,
            metadata,
            _registered_method=True)

    @staticmethod
    def resume(request,
            target,
            options=(),
            channel_credentials=None,
            call_credentials=None,
            insecure=False,
            compression=None,
            wait_for_ready=None,
            timeout=None,
            metadata=None):
        return grpc.experimental.unary_unary(
            request,
            target,
            '/ai.stigmer.agentic.workflowexecution.v1.WorkflowExecutionCommandController/resume',
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_io__pb2.ResumeWorkflowExecutionInput.SerializeToString,
            ai_dot_stigmer_dot_agentic_dot_workflowexecution_dot_v1_dot_api__pb2.WorkflowExecution.FromString,
            options,
            channel_credentials,
            insecure,
            call_credentials,
            compression,
            wait_for_ready,
            timeout,
            metadata,
            _registered_method=True)
