Here is the refactored package structure. I have organized the code to ensure single responsibilities, handle circular dependencies using `TYPE_CHECKING`, and provide a clean public API.

### Directory Structure
```
queue/
├── __init__.py
├── core.py
├── priority.py
└── retry.py
```

### 1. `queue/__init__.py`
This file defines the public API. It imports the main classes and functions from the internal modules so users can access them directly via `from queue import Job, JobQueue`.

```python
from .core import Job, JobQueue
from .retry import process_with_retry

__all__ = ["Job", "JobQueue", "process_with_retry"]
```

### 2. `queue/core.py`
This module contains the data model (`Job`) and the primary container logic (`JobQueue`). It imports `PriorityQueue` from the `priority` module to manage storage.

```python
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Optional
from .priority import PriorityQueue

class JobStatus(Enum):
    PENDING = "pending"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"

@dataclass
class Job:
    """Represents a single unit of work."""
    id: str
    payload: Any
    priority: int = 0
    status: JobStatus = JobStatus.PENDING
    attempts: int = 0
    created_at: float = field(default_factory=lambda: __import__('time').time())

    def __lt__(self, other):
        """
        Comparison for priority queue.
        Lower priority number = higher priority.
        If priorities are equal, earlier creation time wins.
        """
        if self.priority != other.priority:
            return self.priority < other.priority
        return self.created_at < other.created_at

class JobQueue:
    """
    Main orchestrator for the job queue.
    Delegates ordering to PriorityQueue.
    """
    def __init__(self):
        self._pq = PriorityQueue()

    def enqueue(self, job: Job) -> None:
        """Add a job to the queue."""
        self._pq.push(job)

    def dequeue(self) -> Optional[Job]:
        """Remove and return the highest priority job."""
        return self._pq.pop()

    def is_empty(self) -> bool:
        """Check if the queue has no jobs."""
        return self._pq.is_empty()
```

### 3. `queue/priority.py`
This module handles the heap-based implementation. It uses `TYPE_CHECKING` to import `Job` only for type hints, preventing circular imports at runtime.

```python
import heapq
from typing import Any, Optional, TYPE_CHECKING

if TYPE_CHECKING:
    from .core import Job

class PriorityQueue:
    """
    Internal priority queue implementation using heapq.
    Stores tuples of (priority, counter, job) to handle ties.
    """
    def __init__(self):
        self._heap: list = []
        self._counter: int = 0

    def push(self, job: "Job") -> None:
        """Push a job onto the heap."""
        # Counter ensures FIFO stability for same priority jobs
        heapq.heappush(self._heap, (job.priority, self._counter, job))
        self._counter += 1

    def pop(self) -> Optional["Job"]:
        """Pop the highest priority job from the heap."""
        if self._heap:
            return heapq.heappop(self._heap)[2]
        return None

    def peek(self) -> Optional["Job"]:
        """View the highest priority job without removing it."""
        if self._heap:
            return self._heap[0][2]
        return None

    def is_empty(self) -> bool:
        """Check if the heap is empty."""
        return len(self._heap) == 0
```

### 4. `queue/retry.py`
This module encapsulates the retry logic, including exponential backoff. It imports `Job` and `JobStatus` from `core`.

```python
import random
import time
from typing import Callable, Any, Tuple

from .core import Job, JobStatus

def process_with_retry(
    job: Job,
    function: Callable[[Any], Any],
    max_retries: int = 3,
    base_delay: float = 1.0,
    max_delay: float = 60.0
) -> Tuple[JobStatus, float]:
    """
    Executes a function with a job payload, handling failures with exponential backoff.
    
    Args:
        job: The job object containing ID and payload.
        function: The callable to execute with job.payload.
        max_retries: Maximum number of attempts.
        base_delay: Base delay for backoff calculation.
        max_delay: Cap for the maximum backoff duration.
        
    Returns:
        A tuple of (JobStatus, delay_to_wait).
    """
    # If we've exceeded max attempts, fail immediately
    if job.attempts >= max_retries:
        job.status = JobStatus.FAILED
        return job.status, 0.0

    try:
        function(job.payload)
        job.status = JobStatus.COMPLETED
        job.attempts = 0  # Reset attempts on success
        return job.status, 0.0
        
    except Exception as e:
        job.attempts += 1
        if job.attempts >= max_retries:
            job.status = JobStatus.FAILED
            return job.status, 0.0
        else:
            job.status = JobStatus.PENDING
            # Exponential backoff with jitter
            delay = base_delay * (2 ** (job.attempts - 1))
            delay = min(delay, max_delay)
            jitter = random.uniform(0, 0.2) * delay
            final_delay = delay + jitter
            
            # In a real async system, we wouldn't sleep here.
            # This returns the delay so the caller can schedule the retry.
            return job.status, final_delay
```

### Key Refactoring Decisions

1.  **Circular Import Handling**: `core.py` imports `PriorityQueue` from `priority.py`, and `priority.py` needs to know about `Job` for type hints. I used `TYPE_CHECKING` in `priority.py` to ensure `Job` is only imported during static analysis, not at runtime.
2.  **Single Responsibility**:
    *   `core.py`: Defines the data model (`Job`) and the main container (`JobQueue`).
    *   `priority.py`: Only handles heap operations.
    *   `retry.py`: Only handles failure recovery logic.
3.  **Public API**: The `__init__.py` hides the internal file structure, allowing users to import `Job` and `JobQueue` directly from the `queue` package without worrying about `core` or `priority` modules.
4.  **Type Safety**: Added type hints throughout, utilizing forward references (strings) where necessary to resolve import order issues.