Here’s the refactored package structure. Each module follows the single-responsibility principle, uses relative imports, and maintains a clean, type-hinted public API.

### `queue/__init__.py`
```python
"""
queue package
Public API for job management, queuing, and retry logic.
"""
from .core import Job, JobQueue
from .priority import PriorityQueue
from .retry import process_with_retry

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

### `queue/core.py`
```python
"""
Core data models and basic FIFO queue implementation.
"""
from __future__ import annotations
import uuid
from collections import deque
from dataclasses import dataclass, field
from typing import Any, Optional


@dataclass
class Job:
    """Represents a unit of work to be processed."""
    id: str = field(default_factory=lambda: str(uuid.uuid4()))
    task: Any = None
    priority: int = 0
    status: str = "pending"
    retries: int = 0
    max_retries: int = 3


class JobQueue:
    """Thread-unsafe FIFO job queue."""
    def __init__(self) -> None:
        self._queue: deque[Job] = deque()

    def enqueue(self, job: Job) -> None:
        """Add a job to the end of the queue."""
        self._queue.append(job)

    def dequeue(self) -> Optional[Job]:
        """Remove and return the first job, or None if empty."""
        if self._queue:
            return self._queue.popleft()
        return None

    def peek(self) -> Optional[Job]:
        """Return the first job without removing it."""
        return self._queue[0] if self._queue else None

    def __len__(self) -> int:
        return len(self._queue)

    def is_empty(self) -> bool:
        return len(self._queue) == 0
```

### `queue/priority.py`
```python
"""
Priority queue implementation using a min-heap.
"""
import heapq
from typing import Optional
from .core import Job


class PriorityQueue:
    """Thread-unsafe priority job queue. Lower priority value = higher precedence."""
    def __init__(self) -> None:
        self._heap: list[tuple[int, int, Job]] = []
        self._counter: int = 0  # Tie-breaker for stable ordering

    def enqueue(self, job: Job) -> None:
        heapq.heappush(self._heap, (job.priority, self._counter, job))
        self._counter += 1

    def dequeue(self) -> Optional[Job]:
        if self._heap:
            _, _, job = heapq.heappop(self._heap)
            return job
        return None

    def peek(self) -> Optional[Job]:
        if self._heap:
            return self._heap[0][2]
        return None

    def __len__(self) -> int:
        return len(self._heap)

    def is_empty(self) -> bool:
        return len(self._heap) == 0
```

### `queue/retry.py`
```python
"""
Execution wrapper with exponential backoff retry logic.
"""
import time
from typing import Callable, Any, Optional
from .core import Job


def process_with_retry(
    job: Job,
    process_fn: Callable[[Job], Any],
    max_retries: int = 3,
    base_delay: float = 1.0,
) -> Any:
    """
    Execute a job's task with exponential backoff on failure.
    
    Args:
        job: The job instance to process.
        process_fn: Callable that receives the job and returns a result.
        max_retries: Number of retry attempts after the initial try.
        base_delay: Initial delay in seconds before the first retry.
        
    Returns:
        The result of process_fn on success.
        
    Raises:
        Exception: The last exception raised if all retries are exhausted.
    """
    last_exception: Optional[Exception] = None

    for attempt in range(max_retries + 1):
        try:
            job.status = "processing"
            job.retries = attempt
            result = process_fn(job)
            job.status = "completed"
            return result
        except Exception as e:
            last_exception = e
            job.status = "failed"
            job.retries = attempt + 1

            if attempt < max_retries:
                delay = base_delay * (2 ** attempt)
                time.sleep(delay)
            else:
                raise last_exception

    # Unreachable, but satisfies static type checkers
    raise RuntimeError("Unexpected termination of retry loop")
```

### Key Design Decisions:
1. **Relative Imports**: All cross-module references use `.module` syntax, which is the Python standard for packages and avoids circular import issues.
2. **Interface Consistency**: `JobQueue` and `PriorityQueue` share the same method signature (`enqueue`, `dequeue`, `peek`, `__len__`, `is_empty`) making them interchangeable in consumer code.
3. **Stable Priority Ordering**: `PriorityQueue` uses a monotonic counter as a tie-breaker in the heap tuple, ensuring FIFO behavior for jobs with identical priorities.
4. **Retry Transparency**: `process_with_retry` mutates the `Job` status/retry count in-place so callers can inspect execution history without extra return values.
5. **Type Safety**: Full type hints with `from __future__ import annotations` for forward-compatibility and cleaner syntax.

You can now import and use the package like:
```python
from queue import Job, JobQueue, PriorityQueue, process_with_retry

q = PriorityQueue()
q.enqueue(Job(task=lambda: print("Work"), priority=1))
job = q.dequeue()
process_with_retry(job, process_fn=lambda j: j.task())
```