# PEP 639 SPDX license expression (`license = "MIT"` below) requires
# setuptools>=77. Keep this floor in lockstep with the `license` form in
# [project]; an older build backend rejects the string form.
[build-system]
requires = ["setuptools>=77.0,<83"]
build-backend = "setuptools.build_meta"

[project]
name = "hermes-agent"
version = "0.17.0"
description = "The self-improving AI agent — creates skills from experience, improves them during use, and runs anywhere"
readme = "README.md"
# Upper bound is load-bearing, not cosmetic. uv resolves the project's
# Python from `requires-python`, and an inherited `UV_PYTHON` env var (or a
# fresh distro whose newest interpreter uv auto-picks) will otherwise select
# 3.14, where Rust-backed transitives (e.g. pydantic-core) have no cp314
# wheel yet and fall back to a maturin source build that fails. Capping at
# <3.14 makes uv refuse 3.14 with a clear error instead of attempting that
# build. Raise the ceiling once our Rust transitives ship cp314 wheels.
requires-python = ">=3.11,<3.14"
authors = [{ name = "Nous Research" }]
license = "MIT"
license-files = ["LICENSE"]
dependencies = [
  # Core — every direct dep is exact-pinned to ==X.Y.Z (no ranges).
  # Rationale: ranges allow PyPI to ship a fresh version of a transitive
  # at any time without a code review on our side. Exact pins mean the
  # only way a new package version reaches a user is via an intentional
  # update on our end (bump the pin in this file, regenerate uv.lock).
  # This was tightened on 2026-05-12 in response to the Mini Shai-Hulud
  # worm hitting mistralai 2.4.6 on PyPI; if that release had been
  # captured by `mistralai>=2.3.0,<3` rather than an exact pin, every
  # install in the hours before the quarantine would have pulled it.
  #
  # When updating: bump the version below AND regenerate uv.lock with
  # `uv lock` so the transitive resolution stays consistent. Don't
  # introduce ranges back without a written justification.
  #
  # Scope rule: only packages used by EVERY hermes session belong here.
  # Anything that's provider-specific (`anthropic`, `firecrawl-py`,
  # `exa-py`, `fal-client`, `edge-tts`, `parallel-web`) belongs in an
  # extra and gets lazy-installed via `tools/lazy_deps.py` when the
  # user picks that backend. Smaller `dependencies` = smaller blast
  # radius for the next supply-chain attack.
  "openai==2.24.0",
  "certifi==2026.5.20",
  "python-dotenv==1.2.2",
  "fire==0.7.1",
  "httpx[socks]==0.28.1",
  "rich==14.3.3",
  "tenacity==9.1.4",
  "pyyaml==6.0.3",
  "ruamel.yaml==0.18.17",
  "requests==2.33.0",  # CVE-2026-25645
  "jinja2==3.1.6",
  # Bumped from 2.12.5 to 2.13.4 to pull in pydantic-core 2.46.4.
  # pydantic-core 2.41.5 (pulled by 2.12.5) segfaults when the OpenAI SDK's
  # Responses API resource is exercised from a non-main thread, which is the
  # codex_responses dispatch in agent/chat_completion_helpers.py:_call.
  "pydantic==2.13.4",
  # Interactive CLI (prompt_toolkit is used directly by cli.py)
  "prompt_toolkit==3.0.52",
  # Cron scheduler (built-in feature — scheduled cron/interval jobs use croniter).
  "croniter==6.0.0",
  # ``packaging`` is imported directly on three production paths but was never
  # declared, so it only reached users transitively (pip/uv pull it for other
  # tools). The slim official Docker image ships without it, where the
  # try/except-ImportError fallbacks silently degrade: Hindsight's
  # ``_meets_minimum_version`` disables update_mode='append' (#40503),
  # tools/lazy_deps.py treats every version constraint as satisfied, and
  # hermes_cli/main.py drops to naive requirement parsing. Pure-Python
  # py3-none-any wheel, no compiled extensions — safe to ship everywhere.
  # Pinned to the version already resolved in uv.lock (no resolution churn).
  "packaging==26.0",
  # Markdown -> HTML conversion for rich message delivery (Matrix
  # `formatted_body`, and the `send_message` tool's HTML path). Now on the
  # DEFAULT delivery path, not matrix-specific: without it both
  # gateway/platforms/matrix.py and tools/send_message_tool.py silently fall
  # back to plain text, so cron/agent deliveries render raw `##`/`**`/tables
  # in clients like Element (see #32486). Pure-Python py3-none-any wheel
  # (~108KB, no compiled extensions, no platform constraints), so unlike the
  # matrix extra's `mautrix`/`python-olm` it's safe to ship everywhere — keeps
  # it out of the lazy-install path that exists only for the heavy matrix deps.
  "Markdown==3.10.2",
  # Skills Hub (GitHub App JWT auth — optional, only needed for bot identity)
  "PyJWT[crypto]==2.13.0",  # PYSEC-2026-175/177/178/179
  # urllib3 2.7.0 fixes GHSA-mf9v-mfxr-j63j (decompression-bomb bypass)
  # and GHSA-qccp-gfcp-xxvc (header leak across origins).
  "urllib3>=2.7.0,<3",
  # Windows has no IANA tzdata shipped with the OS, so Python's ``zoneinfo``
  # (PEP 615) raises ``ZoneInfoNotFoundError`` for every non-UTC timezone
  # out of the box.  ``tzdata`` ships the Olson database as a data package
  # Python resolves automatically.  No-op on Linux/macOS (which have
  # /usr/share/zoneinfo).  Credits: PR #13182 (@sprmn24).
  "tzdata==2025.3; sys_platform == 'win32'",
  # Cross-platform process / PID management.  `psutil` is the canonical
  # answer for "is this PID alive" and process-tree walking across Linux,
  # macOS and Windows.  It replaces POSIX-only idioms like `os.kill(pid, 0)`
  # (which is a silent killer on Windows — see CONTRIBUTING.md) and
  # `os.killpg` (which doesn't exist on Windows).
  "psutil==7.2.2",
  # Browser CDP supervisor + browser_dialog import this directly. Keep core
  # so browser tool discovery doesn't fail on lean installs.
  "websockets==15.0.1",
  # .gitignore-aware file matching for desktop build stamp.
  "pathspec==1.1.1",
  "fastapi>=0.104.0,<1",
  "uvicorn[standard]>=0.24.0,<1",
  # Streaming multipart uploads for the dashboard file manager (NS-501).
  # FastAPI's UploadFile/Form depend on python-multipart; it is NOT pulled in
  # by fastapi itself, so the dashboard's multipart upload endpoint would 500
  # without an explicit dependency here (and in the `web` extra below).
  "python-multipart>=0.0.9,<1",
  "ptyprocess>=0.7.0,<1; sys_platform != 'win32'",
  "pywinpty>=2.0.0,<3; sys_platform == 'win32'",
  # Image resize recovery for the vision tools. Pillow shrinks oversized images
  # (>5 MB or >8000px) at embed time; without it the byte AND pixel-dimension
  # shrink paths no-op, so an oversized image bakes into immutable history and
  # bricks the session on Anthropic's non-retryable 400. Pure-wheel, no system
  # libs required for the codecs we use, so it's safe to ship in the base
  # install rather than gating it behind an extra + a mid-session lazy install
  # (which deadlocked the CLI under prompt_toolkit — see #40490).
  "Pillow==12.2.0",
  # Windows log rotation. Stdlib ``RotatingFileHandler.doRollover()`` uses
  # ``os.rename()`` which fails with ``PermissionError [WinError 32]`` on
  # Windows whenever any other process holds an append-mode handle on
  # ``agent.log`` (always the case in Hermes — TUI, gateway, ``hy_memory``
  # server, MCP servers, and on-demand CLI commands all log from separate
  # processes), pinning ``agent.log`` at the 5 MiB threshold and spamming
  # stderr on every emit (see #44873). ``concurrent-log-handler`` wraps the
  # rename in a cross-process file lock (via ``portalocker``: pywin32 on
  # Windows) so only one process rotates at a time. ``hermes_logging.py``
  # aliases it ONLY on Windows — POSIX renames an open file fine, so stdlib
  # already works there and managed-mode perms depend on its exact lifecycle.
  # Hence the ``sys_platform == 'win32'`` marker: the dep (and its portalocker
  # / pywin32 tree) ships only where it's actually used.
  "concurrent-log-handler==0.9.29; sys_platform == 'win32'",
]

[project.optional-dependencies]
# Native Anthropic provider — only needed when provider=anthropic (not via
# OpenRouter or other aggregators).
anthropic = ["anthropic==0.87.0"]  # CVE-2026-34450, CVE-2026-34452
# Web search backends — each only loaded when the user picks it as their
# search provider (configured via `hermes tools` or config.yaml).
exa = ["exa-py==2.10.2"]
firecrawl = ["firecrawl-py==4.17.0"]
parallel-web = ["parallel-web==0.4.2"]
# Image generation backends
fal = ["fal-client==0.13.1"]
# Edge TTS — default TTS provider but still optional (users can pick
# ElevenLabs / OpenAI / MiniMax instead).
edge-tts = ["edge-tts==7.2.7"]
modal = ["modal==1.3.4"]
daytona = ["daytona==0.155.0"]
hindsight = ["hindsight-client==0.6.1"]
dev = ["debugpy==1.8.20", "pytest==9.0.2", "pytest-asyncio==1.3.0", "mcp==1.26.0", "starlette==1.0.1", "ty==0.0.21", "ruff==0.15.10", "setuptools==81.0.0"]  # starlette: CVE-2026-48710; setuptools: latest <82 (torch >=2.11 caps setuptools<82)
messaging = ["python-telegram-bot[webhooks]==22.6", "discord.py[voice]==2.7.1", "aiohttp==3.13.4", "brotlicffi==1.2.0.1", "slack-bolt==1.27.0", "slack-sdk==3.40.1", "qrcode==7.4.2"]  # aiohttp: CVE-2026-34513/34518/34519/34520/34525
cron = []  # croniter is now a core dependency; this extra kept for back-compat
slack = ["slack-bolt==1.27.0", "slack-sdk==3.40.1", "aiohttp==3.13.4"]
matrix = ["mautrix[encryption]==0.21.0", "aiosqlite==0.22.1", "asyncpg==0.31.0", "aiohttp-socks==0.11.0"]
# WeCom callback-mode adapter — parses untrusted XML POST bodies from
# WeCom-controlled callback endpoints, so we use defusedxml (drop-in
# replacement for stdlib xml.etree.ElementTree) to block billion-laughs
# and XXE. aiohttp/httpx are already in [messaging]; defusedxml lands
# here to keep the dependency local to wecom_callback's threat model.
wecom = ["defusedxml==0.7.1"]
cli = ["simple-term-menu==1.6.6"]
tts-premium = ["elevenlabs==1.59.0"]
voice = [
  # Local STT pulls in wheel-only transitive deps (ctranslate2, onnxruntime),
  # so keep it out of the base install for source-build packagers like Homebrew.
  "faster-whisper==1.2.1",
  "sounddevice==0.5.5",
  "numpy==2.4.3",
]
pty = [
  # Kept as a no-op back-compat alias — `ptyprocess` and `pywinpty` are now
  # in the main `dependencies` list (with the same platform markers), so
  # any existing `pip install hermes-agent[pty]` invocations resolve cleanly
  # without pulling in extra packages.
]
honcho = ["honcho-ai==2.0.1"]
# Image resize recovery for the vision tools. Pillow is now a CORE dependency
# (see the main `dependencies` list) since the byte/pixel shrink paths are on
# the default vision-embed path and the mid-session lazy install deadlocked the
# CLI under prompt_toolkit (#40490). This extra is kept as a no-op back-compat
# alias so existing `pip install hermes-agent[vision]` invocations still resolve.
vision = []
# CVE-2026-48710 (BadHost): Starlette is pulled transitively by mcp's
# sse-starlette / HTTP-SSE stack (and by fastapi in the `web` extra). Before
# 1.0.1, a malformed Host header makes `request.url.path` desync from the path
# the ASGI router actually dispatched, so middleware/endpoints that gate on
# `request.url` can be bypassed. We pin a patched Starlette directly in every
# extra that exposes a Starlette-backed server surface so pip/uv can't resolve
# a vulnerable pre-1.0.1 transitive. Bump in lockstep with uv.lock.
mcp = ["mcp==1.26.0", "starlette==1.0.1"]  # starlette: CVE-2026-48710
nemo-relay = ["nemo-relay==0.3"]
homeassistant = ["aiohttp==3.13.4"]
sms = ["aiohttp==3.13.4"]
teams = ["microsoft-teams-apps==2.0.13.4", "aiohttp==3.13.4"]
# Computer use — macOS background desktop control via cua-driver (MCP stdio).
# The cua-driver binary itself is installed via `hermes tools` post-setup
# (curl install script); this extra just pins the MCP client used to talk
# to it, which is already provided by the `mcp` extra.
computer-use = ["mcp==1.26.0", "starlette==1.0.1"]  # starlette: CVE-2026-48710
acp = ["agent-client-protocol==0.9.0"]
# mistral: Voxtral STT + TTS. Pinned to an exact verified-clean version.
# The `mistralai` PyPI project was quarantined 2026-05-12 after the malicious
# 2.4.6 release (Mini Shai-Hulud worm); 2.4.6 was removed from PyPI and the
# project is serving clean releases again (2.4.7 2026-05-25, 2.4.8 2026-05-28).
# Like other opt-in TTS/STT backends, this is lazy-installed via
# tools/lazy_deps.py (stt.mistral / tts.mistral) at first use — deliberately
# NOT re-added to [all] so a future quarantined release can't break fresh
# installs (see [all] policy comment below).
mistral = ["mistralai==2.4.8"]
bedrock = ["boto3==1.42.89"]
azure-identity = ["azure-identity==1.25.3"]
termux = [
  # Baseline Android / Termux path for reliable fresh installs.

[166 more lines in file. Use offset=221 to continue.]


dualcoach/profile/pyproject.toml
--- tools
/home/cube/.local/bin/basedpyright
ruff 0.15.10
Python 3.12.9


    leaky = _LeakySidecarRow(
        _CUSTOMER_IDENTITY, date(2026, 8, 23), "submitted", "event-submitted", _digest("row"), _digest("digest"),
        "홍길동", "70.3kg", "2300kcal 식사 기록",
    )
    card = Topic59DayCard.from_sidecar(
        leaky, completed_field_count=5, draft_state=Topic59DraftState.DRAFTED,
        approval_state=Topic59ApprovalState.PENDING, delivery_state=Topic59DeliveryState.NOT_SENT,
        candidate_digest=_CANDIDATE_DIGEST,
    )

    assert set(card.payload()) == _SAFE_KEYS
    assert all(value not in card.render() for value in {leaky.customer_name, leaky.raw_weight, leaky.meal_note})

@dataclass(frozen=True, slots=True)
class _NoopMaintenanceGate:
    def evaluate(self, projection, now) -> Topic59MaintenanceDecision:
        _ = projection, now
        return Topic59MaintenanceDecision(
            kind=Topic59MaintenanceDecisionKind.NOOP_MAINTENANCE,
            audit_digest="9" * 64,
        )

@pytest.mark.anyio
async def test_maintenance_precedes_topic59_claim_and_transport(tmp_path: Path, monkeypatch) -> None:
    # Given
    transport = _Transport()
    projector = Topic59DayCardProjector(tmp_path / "topic59.jsonl", _NoopMaintenanceGate())
    monkeypatch.setattr(
        projector._ledger, "claim",
        lambda projection: (_ for _ in ()).throw(AssertionError(projection.logical_key)),
    )

    # When
    result = await projector.publish(
        _request(date(2026, 8, 20), Topic59DayState.SUBMITTED), transport,
    )

    # Then
    assert result.disposition is Topic59PublicationDisposition.NOOP_MAINTENANCE
    assert result.message_id is None
    assert result.maintenance_audit_digest == "9" * 64
    assert transport.sent == transport.edited == []
    assert not (tmp_path / "topic59.jsonl").exists()


"""Typed aggregate result for one weekly operations dispatch tick."""

from dataclasses import dataclass

from .nutrition_weekly_operations import Topic59PublicationDisposition

@dataclass(frozen=True, slots=True)
class WeeklyOperationsDispatchResult:
    failures: tuple[str, ...]
    topic59_dispositions: tuple[Topic59PublicationDisposition, ...]

    @property
    def maintenance_noop(self) -> bool:
        return (
            not self.failures
            and Topic59PublicationDisposition.NOOP_MAINTENANCE
            in self.topic59_dispositions
        )


"""Typed terminal states and provider outcomes for Topic-59 publication."""

from __future__ import annotations

from dataclasses import dataclass
from enum import StrEnum
from typing import TYPE_CHECKING, Literal

if TYPE_CHECKING:
    from .nutrition_weekly_operations_models import Topic59Projection

class Topic59LedgerError(Exception):
    """The Topic-59 publication history cannot safely advance."""

class Topic59LedgerCorruption(Topic59LedgerError):
    """Stored publication bytes failed their typed integrity contract."""

class Topic59LedgerConflict(Topic59LedgerError):
    """A reserved publication no longer has a reconcilable pending state."""

class Topic59LedgerState(StrEnum):
    PREPARED = "prepared"
    SENDING = "sending"
    DELIVERED = "delivered"
    SENT_AUDITED = "sent_audited"
    UNKNOWN = "unknown"
    FAILED = "failed"
    AUTHORITY_MISMATCH = "authority_mismatch"

class Topic59ProviderFailure(StrEnum):
    REJECTED = "rejected"
    TIMEOUT = "timeout"
    CONNECTION_LOST = "connection_lost"
    RECEIPT_MISMATCH = "receipt_mismatch"

class Topic59IncidentReason(StrEnum):
    AUTHORITY_REJECTED = "authority_rejected"
    MAINTENANCE_REJECTED = "maintenance_rejected"
    AUTHORITY_MISMATCH = "authority_mismatch"
    LEDGER_CORRUPTION = "ledger_corruption"
    MISSING_MESSAGE_ID = "missing_message_id"
    UNRESOLVED_RESERVATION = "unresolved_reservation"
    PROVIDER_TERMINAL = "provider_terminal"

@dataclass(frozen=True, slots=True)
class Topic59ProviderDelivered:
    message_id: str

    def __post_init__(self) -> None:
        if not self.message_id.isdecimal() or len(self.message_id) > 19:
            raise Topic59LedgerError("provider receipt")

@dataclass(frozen=True, slots=True)
class Topic59ProviderKnownFailure:
    reason: Topic59ProviderFailure

@dataclass(frozen=True, slots=True)
class Topic59ProviderUnknown:
    reason: Topic59ProviderFailure

Topic59ProviderOutcome = Topic59ProviderDelivered | Topic59ProviderKnownFailure | Topic59ProviderUnknown

@dataclass(frozen=True, slots=True)
class Topic59LedgerAction:
    kind: Literal["send", "edit", "noop", "incident"]
    projection: Topic59Projection
    operation: Literal["send", "edit"] | None = None
    message_id: str | None = None
    reason: Topic59IncidentReason | None = None


**Planning negative matrix test isolation**

**Configuring test environment and running affected tests**


........................................................................ [ 38%]
........................................................................ [ 77%]
...........................................                              [100%]
187 passed in 6.60s


No diagnostics found


error[basedpyright] (reportMissingImports) at 26:5: 가져오기 ".nutrition_weekly_maintenance_io"을(를) 확인할 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 27:23: "_atomic_write_at" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 28:23: "_fsync_directory" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 29:17: "_open_file" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 30:28: "_open_owned_directory" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 31:22: "_parse_document" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 32:23: "_read_credential" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 33:23: "_read_regular_at" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 68:12: "authority" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 76:12: "directory_fd" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownMemberType) at 78:15: "hold" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownMemberType) at 78:15: "kst_day" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownArgumentType) at 84:41: 인수 형식을 알 수 없습니다.
  인수가 "_evaluate_locked" 함수의 "directory_fd" 매개 변수에 해당합니다.
error[basedpyright] (reportUnknownArgumentType) at 84:55: 인수 형식을 알 수 없습니다.
  인수가 "_evaluate_locked" 함수의 "authority" 매개 변수에 해당합니다.
error[basedpyright] (reportUnknownArgumentType) at 86:21: 인수 형식을 알 수 없습니다.
  인수가 "close" 함수의 "fd" 매개 변수에 해당합니다.
error[basedpyright] (reportUnknownVariableType) at 98:12: "lock_fd" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownArgumentType) at 100:28: 인수 형식을 알 수 없습니다.
  인수가 "flock" 함수의 "fd" 매개 변수에 해당합니다.
error[basedpyright] (reportUnknownArgumentType) at 103:28: 인수 형식을 알 수 없습니다.
  인수가 "flock" 함수의 "fd" 매개 변수에 해당합니다.
error[basedpyright] (reportUnknownArgumentType) at 104:25: 인수 형식을 알 수 없습니다.
  인수가 "close" 함수의 "fd" 매개 변수에 해당합니다.
error[basedpyright] (reportUnknownVariableType) at 168:8: "raw" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 171:11: 반환 유형을 알 수 없습니다.

"_atomic_write_at" 유형을 알 수 없습니다.

"_fsync_directory" 유형을 알 수 없습니다.

"_open_file" 유형을 알 수 없습니다.

"_open_owned_directory" 유형을 알 수 없습니다.

"_parse_document" 유형을 알 수 없습니다.

"_read_credential" 유형을 알 수 없습니다.

"_read_regular_at" 유형을 알 수 없습니다.

"authority" 유형을 알 수 없습니다.

"directory_fd" 유형을 알 수 없습니다.

"hold" 유형을 알 수 없습니다.

"kst_day" 유형을 알 수 없습니다.

"lock_fd" 유형을 알 수 없습니다.

"raw" 유형을 알 수 없습니다.

반환 유형을 알 수 없습니다.


error[basedpyright] (reportMissingImports) at 25:5: 가져오기 ".nutrition_weekly_dispatch_result"을(를) 확인할 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 25:46: "WeeklyOperationsDispatchResult" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownParameterType) at 266:10: 반환 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 285:11: 반환 유형을 알 수 없습니다.

반환 유형을 알 수 없습니다.

반환 유형을 알 수 없습니다.


error[basedpyright] (reportMissingImports) at 23:9: 가져오기 "gateway.platforms.nutrition_weekly_dispatch_result"을(를) 확인할 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 24:8: "WeeklyOperationsDispatchResult" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownParameterType) at 156:14: 반환 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 165:12: "dispatch_weekly_operations_tick"의 형식을 부분적으로 알 수 없습니다.
  ‘dispatch_weekly_operations_tick’의 유형이 ‘(host: WeeklyOperationsHost, coordinator: WeeklyOperationsCoordinator, tasks: Sequence[WeeklyOperationsTask], *, local_now: datetime) -> CoroutineType[Any, Any, Unknown]’입니다.
error[basedpyright] (reportUnknownVariableType) at 168:15: 반환 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownParameterType) at 172:14: 반환 유형을 알 수 없습니다.
error[basedpyright] (reportMissingImports) at 177:13: 가져오기 "gateway.platforms.nutrition_weekly_dispatch_result"을(를) 확인할 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 178:12: "WeeklyOperationsDispatchResult" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 186:19: 반환 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownVariableType) at 189:8: "dispatch" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownMemberType) at 189:25: "_dispatch_weekly_operations_tick"의 형식을 부분적으로 알 수 없습니다.
  ‘_dispatch_weekly_operations_tick’의 유형이 ‘(coordinator: WeeklyOperationsCoordinator, tasks: Sequence[WeeklyOperationsTask], *, local_now: datetime) -> CoroutineType[Any, Any, Unknown]’입니다.
error[basedpyright] (reportUnknownVariableType) at 192:8: "failures"의 형식을 부분적으로 알 수 없습니다.
  ‘failures’의 유형이 ‘list[Unknown]’입니다.
error[basedpyright] (reportUnknownMemberType) at 192:24: "failures" 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownArgumentType) at 192:24: 인수 형식을 알 수 없습니다.
  인수가 "__init__" 함수의 "iterable" 매개 변수에 해당합니다.
error[basedpyright] (reportUnknownMemberType) at 193:8: "extend"의 형식을 부분적으로 알 수 없습니다.
  ‘extend’의 유형이 ‘(iterable: Iterable[Unknown], /) -> None’입니다.
error[basedpyright] (reportUnknownVariableType) at 200:15: 반환 유형을 알 수 없습니다.
error[basedpyright] (reportUnknownArgumentType) at 201:18: 인수 형식을 부분적으로 알 수 없습니다.
  인수가 "__new__" 함수의 "iterable" 매개 변수에 해당합니다.
  인수 형식이 "list[Unknown]"입니다.
error[basedpyright] (reportUnknownMemberType) at 201:29: "topic59_dispositions" 유형을 알 수 없습니다.

반환 유형을 알 수 없습니다.

반환 유형을 알 수 없습니다.

반환 유형을 알 수 없습니다.

반환 유형을 알 수 없습니다.

"dispatch" 유형을 알 수 없습니다.

"failures" 유형을 알 수 없습니다.

반환 유형을 알 수 없습니다.

"topic59_dispositions" 유형을 알 수 없습니다.