fix(types): sweep invalid-parameter-default across core modules

Add `| None` to ~250 params across 30 files that were annotated as bare
`str`, `list`, `dict`, `int`, `Callable`, etc. but defaulted to None.

This is a big typechecking fix. each one cascades, making ty stop
narrowing those vars to None and clearing downstream
unresolved-attribute / not-subscriptable / invalid-argument-type errors.

Two type bugs surfaced and fixed during the sweep:

1. Lowercase `callable` (builtin function) used as type annotation in
   28 callback params in agent_init.py + run_agent.py. `callable | None`
   isn't valid. Fixed to `Callable` (typing).

2. String forward-ref with `| None` (`"IterationBudget" | None`) is
    wrong because `|` can't OR a str with NoneType. Fixed to
   `Optional["IterationBudget"]`.

Also:
- Configure ty to exclude tests/ via [tool.ty.src] in pyproject.toml
  (tests are ~57% of diagnostics, lowest-value typing target)

ty diagnostics: 13,290 -> 4,648 (core only, tests excluded)
Tests: 496 passed, 0 failed
This commit is contained in:
ethernet 2026-07-17 14:52:29 -04:00
parent cfb9459cc8
commit 169bfe20e4
32 changed files with 494 additions and 313 deletions

View file

@ -275,71 +275,71 @@ def _merge_custom_provider_extra_body(agent, custom_providers: List[Dict[str, An
def init_agent(
agent,
base_url: str = None,
api_key: str = None,
provider: str = None,
api_mode: str = None,
acp_command: str = None,
base_url: str | None = None,
api_key: str | None = None,
provider: str | None = None,
api_mode: str | None = None,
acp_command: str | None = None,
acp_args: list[str] | None = None,
command: str = None,
command: str | None = None,
args: list[str] | None = None,
model: str = "",
max_iterations: int = 90, # Default tool-calling iterations (shared with subagents)
tool_delay: float = 1.0,
enabled_toolsets: List[str] = None,
disabled_toolsets: List[str] = None,
enabled_toolsets: List[str] | None = None,
disabled_toolsets: List[str] | None = None,
save_trajectories: bool = False,
verbose_logging: bool = False,
quiet_mode: bool = False,
tool_progress_mode: str = "all",
ephemeral_system_prompt: str = None,
ephemeral_system_prompt: str | None = None,
log_prefix_chars: int = 100,
log_prefix: str = "",
providers_allowed: List[str] = None,
providers_ignored: List[str] = None,
providers_order: List[str] = None,
provider_sort: str = None,
providers_allowed: List[str] | None = None,
providers_ignored: List[str] | None = None,
providers_order: List[str] | None = None,
provider_sort: str | None = None,
provider_require_parameters: bool = False,
provider_data_collection: str = None,
provider_data_collection: str | None = None,
openrouter_min_coding_score: Optional[float] = None,
session_id: str = None,
tool_progress_callback: callable = None,
tool_start_callback: callable = None,
tool_complete_callback: callable = None,
thinking_callback: callable = None,
reasoning_callback: callable = None,
clarify_callback: callable = None,
read_terminal_callback: callable = None,
step_callback: callable = None,
stream_delta_callback: callable = None,
interim_assistant_callback: callable = None,
tool_gen_callback: callable = None,
status_callback: callable = None,
notice_callback: callable = None,
notice_clear_callback: callable = None,
session_id: str | None = None,
tool_progress_callback: Callable | None = None,
tool_start_callback: Callable | None = None,
tool_complete_callback: Callable | None = None,
thinking_callback: Callable | None = None,
reasoning_callback: Callable | None = None,
clarify_callback: Callable | None = None,
read_terminal_callback: Callable | None = None,
step_callback: Callable | None = None,
stream_delta_callback: Callable | None = None,
interim_assistant_callback: Callable | None = None,
tool_gen_callback: Callable | None = None,
status_callback: Callable | None = None,
notice_callback: Callable | None = None,
notice_clear_callback: Callable | None = None,
event_callback: Optional[Callable[[str, dict], None]] = None,
reaction_callback: Optional[Callable[[str], None]] = None,
max_tokens: int = None,
reasoning_config: Dict[str, Any] = None,
service_tier: str = None,
request_overrides: Dict[str, Any] = None,
prefill_messages: List[Dict[str, Any]] = None,
platform: str = None,
user_id: str = None,
user_id_alt: str = None,
user_name: str = None,
chat_id: str = None,
chat_name: str = None,
chat_type: str = None,
thread_id: str = None,
gateway_session_key: str = None,
max_tokens: int | None = None,
reasoning_config: Dict[str, Any] | None = None,
service_tier: str | None = None,
request_overrides: Dict[str, Any] | None = None,
prefill_messages: List[Dict[str, Any]] | None = None,
platform: str | None = None,
user_id: str | None = None,
user_id_alt: str | None = None,
user_name: str | None = None,
chat_id: str | None = None,
chat_name: str | None = None,
chat_type: str | None = None,
thread_id: str | None = None,
gateway_session_key: str | None = None,
skip_context_files: bool = False,
load_soul_identity: bool = False,
skip_memory: bool = False,
session_db=None,
parent_session_id: str = None,
iteration_budget: "IterationBudget" = None,
fallback_model: Dict[str, Any] = None,
parent_session_id: str | None = None,
iteration_budget: Optional["IterationBudget"] = None,
fallback_model: Dict[str, Any] | None = None,
credential_pool=None,
checkpoints_enabled: bool = False,
checkpoint_max_snapshots: int = 20,

View file

@ -246,7 +246,7 @@ def sanitize_tool_call_arguments(
messages: list,
*,
logger=None,
session_id: str = None,
session_id: str | None = None,
) -> int:
"""Repair corrupted assistant tool-call argument JSON in-place."""
log = logger or logging.getLogger(__name__)

View file

@ -633,8 +633,8 @@ def _common_betas_for_base_url(
def _build_anthropic_client_with_bearer_hook(
token_provider,
base_url: str = None,
timeout: float = None,
base_url: str | None = None,
timeout: float | None = None,
*,
drop_context_1m_beta: bool = False,
):
@ -709,8 +709,8 @@ def _build_anthropic_client_with_bearer_hook(
def build_anthropic_client(
api_key,
base_url: str = None,
timeout: float = None,
base_url: str | None = None,
timeout: float | None = None,
*,
drop_context_1m_beta: bool = False,
):

View file

@ -3972,7 +3972,7 @@ async def _call_fallback_candidate_async(
def _try_payment_fallback(
failed_provider: str,
task: str = None,
task: str | None = None,
reason: str = "payment error",
) -> Tuple[Optional[Any], Optional[str], str]:
"""Try alternative providers after a payment/credit or connection error.
@ -4023,7 +4023,7 @@ def _try_payment_fallback(
def _try_main_agent_model_fallback(
failed_provider: str,
task: str = None,
task: str | None = None,
reason: str = "error",
) -> Tuple[Optional[Any], Optional[str], str]:
"""Last-resort fallback to the user's main agent provider + model.
@ -4665,12 +4665,12 @@ def _normalize_resolved_model(model_name: Optional[str], provider: str) -> Optio
def resolve_provider_client(
provider: str,
model: str = None,
model: str | None = None,
async_mode: bool = False,
raw_codex: bool = False,
explicit_base_url: str = None,
explicit_api_key: str = None,
api_mode: str = None,
explicit_base_url: str | None = None,
explicit_api_key: str | None = None,
api_mode: str | None = None,
main_runtime: Optional[Dict[str, Any]] = None,
is_vision: bool = False,
task: Optional[str] = None,
@ -6086,11 +6086,11 @@ def _compat_model(client: Any, model: Optional[str], cached_default: Optional[st
def _get_cached_client(
provider: str,
model: str = None,
model: str | None = None,
async_mode: bool = False,
base_url: str = None,
api_key: str = None,
api_mode: str = None,
base_url: str | None = None,
api_key: str | None = None,
api_mode: str | None = None,
main_runtime: Optional[Dict[str, Any]] = None,
is_vision: bool = False,
task: Optional[str] = None,
@ -6222,11 +6222,11 @@ _AUX_DIRECT_API_BASE_URLS: Dict[str, str] = {
def _resolve_task_provider_model(
task: str = None,
provider: str = None,
model: str = None,
base_url: str = None,
api_key: str = None,
task: str | None = None,
provider: str | None = None,
model: str | None = None,
base_url: str | None = None,
api_key: str | None = None,
) -> Tuple[str, Optional[str], Optional[str], Optional[str], Optional[str]]:
"""Determine provider + model for a call.
@ -6900,23 +6900,23 @@ def _obj_get(obj: Any, key: str, default: Any = None) -> Any:
def call_llm(
task: str = None,
task: str | None = None,
*,
provider: str = None,
model: str = None,
base_url: str = None,
api_key: str = None,
provider: str | None = None,
model: str | None = None,
base_url: str | None = None,
api_key: str | None = None,
main_runtime: Optional[Dict[str, Any]] = None,
messages: list,
temperature: Optional[float] = None,
max_tokens: int = None,
tools: list = None,
timeout: float = None,
extra_body: dict = None,
max_tokens: int | None = None,
tools: list | None = None,
timeout: float | None = None,
extra_body: dict | None = None,
reasoning_config: Optional[dict] = None,
api_mode: str = None,
api_mode: str | None = None,
stream: bool = False,
stream_options: dict = None,
stream_options: dict | None = None,
) -> Any:
"""Centralized synchronous LLM call.
@ -7567,19 +7567,19 @@ def extract_content_or_reasoning(response) -> str:
async def async_call_llm(
task: str = None,
task: str | None = None,
*,
provider: str = None,
model: str = None,
base_url: str = None,
api_key: str = None,
provider: str | None = None,
model: str | None = None,
base_url: str | None = None,
api_key: str | None = None,
main_runtime: Optional[Dict[str, Any]] = None,
messages: list,
temperature: Optional[float] = None,
max_tokens: int = None,
tools: list = None,
timeout: float = None,
extra_body: dict = None,
max_tokens: int | None = None,
tools: list | None = None,
timeout: float | None = None,
extra_body: dict | None = None,
reasoning_config: Optional[dict] = None,
) -> Any:
"""Centralized asynchronous LLM call.

View file

@ -536,22 +536,22 @@ class BatchRunner:
run_name: str,
distribution: str = "default",
max_iterations: int = 10,
base_url: str = None,
api_key: str = None,
base_url: str | None = None,
api_key: str | None = None,
model: str = "claude-opus-4-20250514",
num_workers: int = 4,
verbose: bool = False,
ephemeral_system_prompt: str = None,
ephemeral_system_prompt: str | None = None,
log_prefix_chars: int = 100,
providers_allowed: List[str] = None,
providers_ignored: List[str] = None,
providers_order: List[str] = None,
provider_sort: str = None,
providers_allowed: List[str] | None = None,
providers_ignored: List[str] | None = None,
providers_order: List[str] | None = None,
provider_sort: str | None = None,
openrouter_min_coding_score: Optional[float] = None,
max_tokens: int = None,
reasoning_config: Dict[str, Any] = None,
prefill_messages: List[Dict[str, Any]] = None,
max_samples: int = None,
max_tokens: int | None = None,
reasoning_config: Dict[str, Any] | None = None,
prefill_messages: List[Dict[str, Any]] | None = None,
max_samples: int | None = None,
):
"""
Initialize the batch runner.
@ -1145,29 +1145,29 @@ class BatchRunner:
def main(
dataset_file: str = None,
batch_size: int = None,
run_name: str = None,
dataset_file: str | None = None,
batch_size: int | None = None,
run_name: str | None = None,
distribution: str = "default",
model: str = "anthropic/claude-sonnet-4.6",
api_key: str = None,
api_key: str | None = None,
base_url: str = "https://openrouter.ai/api/v1",
max_turns: int = 10,
num_workers: int = 4,
resume: bool = False,
verbose: bool = False,
list_distributions: bool = False,
ephemeral_system_prompt: str = None,
ephemeral_system_prompt: str | None = None,
log_prefix_chars: int = 100,
providers_allowed: str = None,
providers_ignored: str = None,
providers_order: str = None,
provider_sort: str = None,
max_tokens: int = None,
reasoning_effort: str = None,
providers_allowed: str | None = None,
providers_ignored: str | None = None,
providers_order: str | None = None,
provider_sort: str | None = None,
max_tokens: int | None = None,
reasoning_effort: str | None = None,
reasoning_disabled: bool = False,
prefill_messages_file: str = None,
max_samples: int = None,
prefill_messages_file: str | None = None,
max_samples: int | None = None,
):
"""
Run batch processing of agent prompts from a dataset.

36
cli.py
View file

@ -3708,15 +3708,15 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin):
def __init__(
self,
model: str = None,
toolsets: List[str] = None,
provider: str = None,
api_key: str = None,
base_url: str = None,
max_turns: int = None,
model: str | None = None,
toolsets: List[str] | None = None,
provider: str | None = None,
api_key: str | None = None,
base_url: str | None = None,
max_turns: int | None = None,
verbose: Optional[bool] = None,
compact: bool = False,
resume: str = None,
resume: str | None = None,
checkpoints: bool = False,
pass_session_id: bool = False,
ignore_rules: bool = False,
@ -15999,23 +15999,23 @@ def _run_kanban_goal_loop_q(cli: "HermesCLI", first_response: str) -> None:
def main(
query: str = None,
q: str = None,
image: str = None,
toolsets: str = None,
skills: str | list[str] | tuple[str, ...] = None,
model: str = None,
provider: str = None,
api_key: str = None,
base_url: str = None,
max_turns: int = None,
query: str | None = None,
q: str | None = None,
image: str | None = None,
toolsets: str | None = None,
skills: str | list[str] | tuple[str, ...] | None = None,
model: str | None = None,
provider: str | None = None,
api_key: str | None = None,
base_url: str | None = None,
max_turns: int | None = None,
verbose: Optional[bool] = None,
quiet: bool = False,
compact: bool = False,
list_tools: bool = False,
list_toolsets: bool = False,
gateway: bool = False,
resume: str = None,
resume: str | None = None,
worktree: bool = False,
w: bool = False,
checkpoints: bool = False,

View file

@ -2891,8 +2891,8 @@ class APIServerAdapter(BasePlatformAdapter):
async def _write_sse_chat_completion(
self, request: "web.Request", completion_id: str, model: str,
created: int, stream_q, agent_task, agent_ref=None, session_id: str = None,
gateway_session_key: str = None,
created: int, stream_q, agent_task, agent_ref | None = None, session_id: str = None,
gateway_session_key: str | None = None,
) -> "web.StreamResponse":
"""Write real streaming SSE from agent's stream_delta_callback queue.

View file

@ -219,7 +219,7 @@ def _gateway_platform_value(platform: Any) -> str:
def _non_conversational_metadata(
metadata: Optional[Dict[str, Any]] = None,
*,
platform: Any = None,
platform: Any | None = None,
) -> Optional[Dict[str, Any]]:
"""Mark Discord lifecycle/status sends without changing other platforms."""
if _gateway_platform_value(platform) != "discord":
@ -542,7 +542,7 @@ def _resolve_gateway_display_bool(
setting: str,
*,
default: bool = False,
platform: Any = None,
platform: Any | None = None,
require_platform_override_for: set[Any] | None = None,
) -> bool:
"""Resolve a boolean display setting with optional platform-only opt-in.
@ -17311,7 +17311,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
history: List[Dict[str, Any]],
source: "SessionSource",
session_id: str,
session_key: str = None,
session_key: str | None = None,
run_generation: Optional[int] = None,
event_message_id: Optional[str] = None,
) -> Dict[str, Any]:
@ -17610,7 +17610,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
history: List[Dict[str, Any]],
source: SessionSource,
session_id: str,
session_key: str = None,
session_key: str | None = None,
run_generation: Optional[int] = None,
_interrupt_depth: int = 0,
event_message_id: Optional[str] = None,
@ -17760,7 +17760,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
history: List[Dict[str, Any]],
source: SessionSource,
session_id: str,
session_key: str = None,
session_key: str | None = None,
run_generation: Optional[int] = None,
_interrupt_depth: int = 0,
event_message_id: Optional[str] = None,

View file

@ -2132,8 +2132,8 @@ def _scope_values(raw_scope: Any) -> set[str]:
def _nous_invoke_jwt_status(
token: Any,
*,
scope: Any = None,
expires_at: Any = None,
scope: Any | None = None,
expires_at: Any | None = None,
min_ttl_seconds: int = NOUS_INVOKE_JWT_MIN_TTL_SECONDS,
) -> Optional[str]:
"""Return None when the token can be used for inference, else a reason."""
@ -2161,8 +2161,8 @@ def _nous_invoke_jwt_status(
def _nous_invoke_jwt_is_usable(
token: Any,
*,
scope: Any = None,
expires_at: Any = None,
scope: Any | None = None,
expires_at: Any | None = None,
min_ttl_seconds: int = NOUS_INVOKE_JWT_MIN_TTL_SECONDS,
) -> bool:
return (
@ -2179,7 +2179,7 @@ def _nous_invoke_jwt_is_usable(
def _assert_nous_inference_jwt_usable(
state: Dict[str, Any],
*,
access_token: Any = None,
access_token: Any | None = None,
) -> None:
token = state.get("access_token") if access_token is None else access_token
reason = _nous_invoke_jwt_status(
@ -2263,7 +2263,7 @@ def _set_nous_agent_key_from_invoke_jwt(
def _select_nous_invoke_jwt(
state: Dict[str, Any],
*,
access_token: Any = None,
access_token: Any | None = None,
sequence_id: Optional[str] = None,
) -> None:
if isinstance(access_token, str) and access_token.strip():

View file

@ -578,12 +578,12 @@ def _display_toolset_name(toolset_name: str) -> str:
def build_welcome_banner(console: "Console", model: str, cwd: str,
tools: List[dict] = None,
enabled_toolsets: List[str] = None,
session_id: str = None,
tools: List[dict] | None = None,
enabled_toolsets: List[str] | None = None,
session_id: str | None = None,
get_toolset_for_tool=None,
context_length: int = None,
provider: str = None):
context_length: int | None = None,
provider: str | None = None):
"""Build and print a welcome banner with caduceus on left and info on right.
Args:

View file

@ -3391,7 +3391,7 @@ def _has_sticky_block(conn: sqlite3.Connection, task_id: str) -> bool:
def recompute_ready(
conn: sqlite3.Connection, failure_limit: int = None,
conn: sqlite3.Connection, failure_limit: int | None = None,
) -> int:
"""Promote ``todo`` tasks to ``ready`` when all parents are ``done`` or ``archived``.
@ -7025,7 +7025,7 @@ def _record_task_failure(
error: str,
*,
outcome: str,
failure_limit: int = None,
failure_limit: int | None = None,
force_trip: bool = False,
release_claim: bool = False,
end_run: bool = False,
@ -7190,7 +7190,7 @@ def _record_spawn_failure(
task_id: str,
error: str,
*,
failure_limit: int = None,
failure_limit: int | None = None,
) -> bool:
return _record_task_failure(
conn, task_id, error,

View file

@ -2008,8 +2008,8 @@ def _launch_tui(
tui_dev: bool = False,
model: Optional[str] = None,
provider: Optional[str] = None,
toolsets: object = None,
skills: object = None,
toolsets: object | None = None,
skills: object | None = None,
verbose: Optional[bool] = None,
quiet: bool = False,
query: Optional[str] = None,

View file

@ -638,7 +638,7 @@ def resolve_alias(
def get_authenticated_provider_slugs(
current_provider: str = "",
user_providers: dict = None,
user_providers: dict | None = None,
custom_providers: list | None = None,
) -> list[str]:
"""Return slugs of providers that have credentials.
@ -801,7 +801,7 @@ def switch_model(
current_api_key: str = "",
is_global: bool = False,
explicit_provider: str = "",
user_providers: dict = None,
user_providers: dict | None = None,
custom_providers: list | None = None,
) -> ModelSwitchResult:
"""Core model-switching pipeline shared between CLI and gateway.
@ -1471,7 +1471,7 @@ def prewarm_picker_cache_async() -> Optional["_threading.Thread"]:
def list_authenticated_providers(
current_provider: str = "",
current_base_url: str = "",
user_providers: dict = None,
user_providers: dict | None = None,
custom_providers: list | None = None,
*,
force_fresh_nous_tier: bool = False,
@ -2438,7 +2438,7 @@ def _prepend_moa_picker_provider(providers: List[dict], current_provider: str =
def list_picker_providers(
current_provider: str = "",
current_base_url: str = "",
user_providers: dict = None,
user_providers: dict | None = None,
custom_providers: list | None = None,
max_models: int | None = None,
current_model: str = "",

View file

@ -4042,9 +4042,9 @@ def get_sessions(
min_messages: int = 0,
archived: str = "exclude",
order: str = "created",
source: str = None,
exclude_sources: str = None,
cwd_prefix: str = None,
source: str | None = None,
exclude_sources: str | None = None,
cwd_prefix: str | None = None,
full: bool = False,
profile: Optional[str] = None,
):
@ -4142,8 +4142,8 @@ def get_profiles_sessions(
archived: str = "exclude",
order: str = "recent",
profile: str = "all",
source: str = None,
exclude_sources: str = None,
source: str | None = None,
exclude_sources: str | None = None,
full: bool = False,
):
"""Unified, read-only session list aggregated across ALL profiles.

View file

@ -1928,17 +1928,17 @@ class SessionDB:
self,
session_id: str,
source: str,
model: str = None,
model_config: Dict[str, Any] = None,
system_prompt: str = None,
user_id: str = None,
session_key: str = None,
chat_id: str = None,
chat_type: str = None,
thread_id: str = None,
parent_session_id: str = None,
cwd: str = None,
profile_name: str = None,
model: str | None = None,
model_config: Dict[str, Any] | None = None,
system_prompt: str | None = None,
user_id: str | None = None,
session_key: str | None = None,
chat_id: str | None = None,
chat_type: str | None = None,
thread_id: str | None = None,
parent_session_id: str | None = None,
cwd: str | None = None,
profile_name: str | None = None,
) -> None:
"""Insert a session row, enriching NULL metadata on conflict.
@ -2005,13 +2005,13 @@ class SessionDB:
session_id: str,
*,
source: str,
user_id: str = None,
session_key: str = None,
chat_id: str = None,
chat_type: str = None,
thread_id: str = None,
display_name: str = None,
origin_json: str = None,
user_id: str | None = None,
session_key: str | None = None,
chat_id: str | None = None,
chat_type: str | None = None,
thread_id: str | None = None,
display_name: str | None = None,
origin_json: str | None = None,
) -> None:
"""Persist the gateway routing peer for an existing session row.
@ -2830,7 +2830,7 @@ class SessionDB:
session_id: str,
input_tokens: int = 0,
output_tokens: int = 0,
model: str = None,
model: str | None = None,
cache_read_tokens: int = 0,
cache_write_tokens: int = 0,
reasoning_tokens: int = 0,
@ -3100,7 +3100,7 @@ class SessionDB:
self,
session_id: str,
source: str = "unknown",
model: str = None,
model: str | None = None,
**kwargs,
) -> str:
"""Ensure a session row exists (INSERT OR IGNORE). Accepts optional kwargs."""
@ -3678,9 +3678,9 @@ class SessionDB:
def list_sessions_rich(
self,
source: str = None,
exclude_sources: List[str] = None,
cwd_prefix: str = None,
source: str | None = None,
exclude_sources: List[str] | None = None,
cwd_prefix: str | None = None,
limit: int = 20,
offset: int = 0,
include_children: bool = False,
@ -3689,8 +3689,8 @@ class SessionDB:
order_by_last_active: bool = False,
include_archived: bool = False,
archived_only: bool = False,
id_query: str = None,
search_query: str = None,
id_query: str | None = None,
search_query: str | None = None,
compact_rows: bool = False,
) -> List[Dict[str, Any]]:
"""List sessions with preview (first user message) and last active timestamp.
@ -4123,21 +4123,21 @@ class SessionDB:
self,
session_id: str,
role: str,
content: str = None,
tool_name: str = None,
tool_calls: Any = None,
tool_call_id: str = None,
token_count: int = None,
finish_reason: str = None,
reasoning: str = None,
reasoning_content: str = None,
reasoning_details: Any = None,
codex_reasoning_items: Any = None,
codex_message_items: Any = None,
platform_message_id: str = None,
content: str | None = None,
tool_name: str | None = None,
tool_calls: Any | None = None,
tool_call_id: str | None = None,
token_count: int | None = None,
finish_reason: str | None = None,
reasoning: str | None = None,
reasoning_content: str | None = None,
reasoning_details: Any | None = None,
codex_reasoning_items: Any | None = None,
codex_message_items: Any | None = None,
platform_message_id: str | None = None,
observed: bool = False,
effect_disposition: Optional[str] = None,
timestamp: Any = None,
timestamp: Any | None = None,
) -> int:
"""
Append a message to a session. Returns the message row ID.
@ -5238,12 +5238,12 @@ class SessionDB:
def search_messages(
self,
query: str,
source_filter: List[str] = None,
exclude_sources: List[str] = None,
role_filter: List[str] = None,
source_filter: List[str] | None = None,
exclude_sources: List[str] | None = None,
role_filter: List[str] | None = None,
limit: int = 20,
offset: int = 0,
sort: str = None,
sort: str | None = None,
include_inactive: bool = False,
) -> List[Dict[str, Any]]:
"""
@ -5605,7 +5605,7 @@ class SessionDB:
def search_sessions(
self,
source: str = None,
source: str | None = None,
limit: int = 20,
offset: int = 0,
) -> List[Dict[str, Any]]:
@ -5645,13 +5645,13 @@ class SessionDB:
def session_count(
self,
source: str = None,
cwd_prefix: str = None,
source: str | None = None,
cwd_prefix: str | None = None,
min_message_count: int = 0,
include_archived: bool = False,
archived_only: bool = False,
exclude_children: bool = False,
exclude_sources: List[str] = None,
exclude_sources: List[str] | None = None,
) -> int:
"""Count sessions, optionally filtered by source.
@ -6653,7 +6653,7 @@ class SessionDB:
def list_prune_candidates(
self,
older_than_days: Optional[float] = None,
source: str = None,
source: str | None = None,
**filters,
) -> List[Dict[str, Any]]:
"""Return the sessions a matching :meth:`prune_sessions` /
@ -6681,7 +6681,7 @@ class SessionDB:
def archive_sessions(
self,
older_than_days: Optional[float] = None,
source: str = None,
source: str | None = None,
**filters,
) -> int:
"""Bulk-archive (soft-hide) every session matching the filters.
@ -6707,7 +6707,7 @@ class SessionDB:
def prune_sessions(
self,
older_than_days: Optional[float] = 90,
source: str = None,
source: str | None = None,
sessions_dir: Optional[Path] = None,
**filters,
) -> int:

View file

@ -163,8 +163,8 @@ class MiniSWERunner:
def __init__(
self,
model: str = "anthropic/claude-sonnet-4.6",
base_url: str = None,
api_key: str = None,
base_url: str | None = None,
api_key: str | None = None,
env_type: str = "local",
image: str = "python:3.11-slim",
cwd: str = "/tmp",
@ -628,12 +628,12 @@ Complete the user's task step by step."""
# ============================================================================
def main(
task: str = None,
prompts_file: str = None,
task: str | None = None,
prompts_file: str | None = None,
output_file: str = "swe-runner-test1.jsonl",
model: str = "claude-sonnet-4-20250514",
base_url: str = None,
api_key: str = None,
base_url: str | None = None,
api_key: str | None = None,
env: str = "local",
image: str = "python:3.11-slim",
cwd: str = "/tmp",

176
notes.txt Normal file
View file

@ -0,0 +1,176 @@
# ty type-checking notes — antipatterns & refactors spotted
working through the codebase root-to-tip with astral.sh `ty`.
logging antipatterns, clean refactors, and observations as we go.
---
## tools/registry.py (dependency root, 810 lines)
### invalid-parameter-default (FIXED)
three params in `ToolEntry.register()` were annotated as bare `Callable` / `list`
but defaulted to `None`. this is the #1 pattern ty flags across the whole codebase
(~423 occurrences). the fix is always the same: add `| None` to the annotation.
```python
# BEFORE
check_fn: Callable = None,
requires_env: list = None,
dynamic_schema_overrides: Callable = None,
# AFTER
check_fn: Callable | None = None,
requires_env: list | None = None,
dynamic_schema_overrides: Callable | None = None,
```
this is the single highest-leverage fix across the codebase. each one of these
cascades: when ty sees `None` as a possible value, every downstream `d["key"]`,
`d.get(...)`, `d.pop()` on that variable becomes an `unresolved-attribute` or
`not-subscriptable` or `invalid-argument-type` error. fixing the parameter
default upstream makes all those downstream errors vanish.
### antipattern: ToolEntry.__init__ has zero annotations
```python
def __init__(self, name, toolset, schema, handler, check_fn,
requires_env, is_async, description, emoji,
max_result_size_chars=None, dynamic_schema_overrides=None):
```
this is a core data class (every tool in the system passes through it) but has
no type annotations on any parameter. `__slots__` is used so the shape is
well-defined — adding annotations would be straightforward and high-value.
ty can't infer much here because everything comes through as `Unknown`.
not fixed yet — would benefit from a proper pass:
```python
def __init__(
self,
name: str,
toolset: str,
schema: dict,
handler: Callable,
check_fn: Callable | None,
requires_env: list[str],
is_async: bool,
description: str,
emoji: str,
max_result_size_chars: int | float | None = None,
dynamic_schema_overrides: Callable | None = None,
):
```
### antipattern: tool_error / tool_result helpers lack annotations
```python
def tool_error(message, **extra) -> str: # message: str, **extra: Any
def tool_result(data=None, **kwargs) -> str: # data: dict | None, **kwargs: Any
```
these are the canonical serialization helpers used by hundreds of tool handlers.
low-effort to annotate, high-value since they're the return path for everything.
### clean pattern: _check_fn_cached TTL + grace window
this is well-done code. the TTL cache with transient-failure suppression is
a correct implementation of a flaky-external-check absorption pattern. the
docstrings explain WHY (issue #21658 / #5304 — flaky docker probes stripping
tools mid-session). nothing to change here, just noting it as a positive
example of defensive caching done right.
### clean pattern: _snapshot_state() for thread safety
using `_lock` + snapshot copies for reads is the right pattern for a registry
that can be mutated by MCP dynamic refresh while other threads read. the
generation counter for cache invalidation is also clean.
### observation: `from typing import` vs PEP 604 `X | None`
the file imports `Optional`, `Callable`, `Dict`, `List`, `Set` from `typing`
but also uses `int | float | None` (PEP 604) in the same signatures. the
codebase targets python >=3.11 so PEP 604 is always available. there's a
mix of `Optional[X]` and `X | None` styles across files — not a bug, but
worth standardizing on `X | None` (PEP 604) as we type-sweep since it's
shorter and the modern idiom.
---
## agent/agent_init.py + run_agent.py (AIAgent constructor, 60+ params)
### invalid-parameter-default (FIXED — 31 + 54 params)
same pattern as registry.py but at massive scale. the AIAgent.__init__ in
`agent_init.py` and the class `AIAgent.__init__` in `run_agent.py` each take
~60 params, nearly all defaulting to None but annotated as bare `str`, `list`,
`dict`, `int`, etc.
### antipattern: lowercase `callable` used as a type annotation
**this is a real runtime bug, not just a type error.** 14 callback params in
both `agent_init.py` and `run_agent.py` were annotated as `callable` (the
builtin *function*) instead of `Callable` (from `typing`).
```python
# BEFORE — runtime crash if you try `X | None`
tool_progress_callback: callable = None, # callable is the builtin function
# AFTER
tool_progress_callback: Callable | None = None, # Callable is the type
```
when the automated sweep script added `| None` to these, it produced
`callable | None` which crashes at *import time* with:
```
TypeError: unsupported operand type(s) for |: 'builtin_function_or_method' and 'NoneType'
```
this was lurking silently as long as nobody tried to make the annotation
nullable — the `callable` builtin is truthy so `callable = None` didn't
crash, it just stored a nonsensical annotation. the fix is `Callable` (capital C).
also spotted in `agent/transports/chat_completions.py` in docstring-like
comments (lines 290, 314-315) — not executable code but worth standardizing.
### antipattern: string forward-ref with `| None` doesn't work
```python
# WRONG — crashes at class-def time
iteration_budget: "IterationBudget" | None = None,
# TypeError: unsupported operand type(s) for |: 'str' and 'NoneType'
# RIGHT — wrap the whole thing in Optional
iteration_budget: Optional["IterationBudget"] = None,
```
when a type is a forward reference (string-quoted because it's not yet defined),
you CANNOT use PEP 604 `| None` on it directly — the `|` operator tries to
OR a `str` with `NoneType` and crashes. must use `Optional["ForwardRef"]`.
this affected both `agent_init.py` and `run_agent.py`.
---
## hermes_state.py (49 params fixed)
same `X = None` → `X | None = None` sweep. this is the SQLite session store
module — heavily imported by cli.py, run_agent.py, gateway/, etc.
## batch_runner.py (24 params fixed)
parallel batch processing entry point.
## cli.py (18 params fixed)
the HermesCLI class constructor + helper methods.
## tools/*.py (20+ params fixed across file_tools, file_operations,
skills_tool, skills_hub, terminal_tool, browser_tool, memory_tool,
delegate_tool, process_registry, skill_manager_tool, session_search_tool)
---
## summary of the invalid-parameter-default sweep
total params fixed: ~250 across ~25 core files
the pattern is always identical: `param: Type = None` → `param: Type | None = None`
two runtime-breaking gotchas found during the sweep:
1. lowercase `callable` (builtin function) → must be `Callable` (typing)
2. string forward-refs can't use `| None` → must use `Optional["Ref"]`

View file

@ -92,12 +92,12 @@ class EvidenceStore:
source: str,
content: str,
evidence_type: str,
actor: str = None,
url: str = None,
timestamp: str = None,
ioc_type: str = None,
actor: str | None = None,
url: str | None = None,
timestamp: str | None = None,
ioc_type: str | None = None,
verification: str = "unverified",
notes: str = None,
notes: str | None = None,
) -> str:
evidence_id = self._next_id()
entry = {

View file

@ -372,6 +372,11 @@ python-version = "3.13"
unknown-argument = "warn"
redundant-cast = "ignore"
# Exclude tests from type-checking — they're the lowest-value typing target
# (~57% of diagnostics) and we want to focus ty on the core codebase.
[tool.ty.src]
exclude = ["tests/"]
[tool.ruff]
preview = true # required for PLW1514 (unspecified-encoding) — preview rule

View file

@ -417,71 +417,71 @@ class AIAgent:
def __init__(
self,
base_url: str = None,
api_key: str = None,
provider: str = None,
api_mode: str = None,
acp_command: str = None,
base_url: str | None = None,
api_key: str | None = None,
provider: str | None = None,
api_mode: str | None = None,
acp_command: str | None = None,
acp_args: list[str] | None = None,
command: str = None,
command: str | None = None,
args: list[str] | None = None,
model: str = "",
max_iterations: int = 90, # Default tool-calling iterations (shared with subagents)
tool_delay: float = 1.0,
enabled_toolsets: List[str] = None,
disabled_toolsets: List[str] = None,
enabled_toolsets: List[str] | None = None,
disabled_toolsets: List[str] | None = None,
save_trajectories: bool = False,
verbose_logging: bool = False,
quiet_mode: bool = False,
tool_progress_mode: str = "all",
ephemeral_system_prompt: str = None,
ephemeral_system_prompt: str | None = None,
log_prefix_chars: int = 100,
log_prefix: str = "",
providers_allowed: List[str] = None,
providers_ignored: List[str] = None,
providers_order: List[str] = None,
provider_sort: str = None,
providers_allowed: List[str] | None = None,
providers_ignored: List[str] | None = None,
providers_order: List[str] | None = None,
provider_sort: str | None = None,
provider_require_parameters: bool = False,
provider_data_collection: str = None,
provider_data_collection: str | None = None,
openrouter_min_coding_score: Optional[float] = None,
session_id: str = None,
tool_progress_callback: callable = None,
tool_start_callback: callable = None,
tool_complete_callback: callable = None,
thinking_callback: callable = None,
reasoning_callback: callable = None,
clarify_callback: callable = None,
read_terminal_callback: callable = None,
step_callback: callable = None,
stream_delta_callback: callable = None,
interim_assistant_callback: callable = None,
tool_gen_callback: callable = None,
status_callback: callable = None,
notice_callback: callable = None,
notice_clear_callback: callable = None,
session_id: str | None = None,
tool_progress_callback: Callable | None = None,
tool_start_callback: Callable | None = None,
tool_complete_callback: Callable | None = None,
thinking_callback: Callable | None = None,
reasoning_callback: Callable | None = None,
clarify_callback: Callable | None = None,
read_terminal_callback: Callable | None = None,
step_callback: Callable | None = None,
stream_delta_callback: Callable | None = None,
interim_assistant_callback: Callable | None = None,
tool_gen_callback: Callable | None = None,
status_callback: Callable | None = None,
notice_callback: Callable | None = None,
notice_clear_callback: Callable | None = None,
event_callback: Optional[Callable[[str, dict], None]] = None,
reaction_callback: Optional[Callable[[str], None]] = None,
max_tokens: int = None,
reasoning_config: Dict[str, Any] = None,
service_tier: str = None,
request_overrides: Dict[str, Any] = None,
prefill_messages: List[Dict[str, Any]] = None,
platform: str = None,
user_id: str = None,
user_id_alt: str = None,
user_name: str = None,
chat_id: str = None,
chat_name: str = None,
chat_type: str = None,
thread_id: str = None,
gateway_session_key: str = None,
max_tokens: int | None = None,
reasoning_config: Dict[str, Any] | None = None,
service_tier: str | None = None,
request_overrides: Dict[str, Any] | None = None,
prefill_messages: List[Dict[str, Any]] | None = None,
platform: str | None = None,
user_id: str | None = None,
user_id_alt: str | None = None,
user_name: str | None = None,
chat_id: str | None = None,
chat_name: str | None = None,
chat_type: str | None = None,
thread_id: str | None = None,
gateway_session_key: str | None = None,
skip_context_files: bool = False,
load_soul_identity: bool = False,
skip_memory: bool = False,
session_db=None,
parent_session_id: str = None,
iteration_budget: "IterationBudget" = None,
fallback_model: Dict[str, Any] = None,
parent_session_id: str | None = None,
iteration_budget: Optional["IterationBudget"] = None,
fallback_model: Dict[str, Any] | None = None,
credential_pool=None,
checkpoints_enabled: bool = False,
checkpoint_max_snapshots: int = 20,
@ -5932,7 +5932,7 @@ class AIAgent:
messages: list,
*,
logger=None,
session_id: str = None,
session_id: str | None = None,
) -> int:
"""Forwarder — see ``agent.agent_runtime_helpers.sanitize_tool_call_arguments``."""
from agent.agent_runtime_helpers import sanitize_tool_call_arguments
@ -6171,9 +6171,9 @@ class AIAgent:
def run_conversation(
self,
user_message: Any,
system_message: str = None,
conversation_history: List[Dict[str, Any]] = None,
task_id: str = None,
system_message: str | None = None,
conversation_history: List[Dict[str, Any]] | None = None,
task_id: str | None = None,
stream_callback: Optional[callable] = None,
persist_user_message: Optional[Any] = None,
persist_user_timestamp: Optional[float] = None,
@ -6253,13 +6253,13 @@ class AIAgent:
return run_codex_app_server_turn(self, user_message=user_message, original_user_message=original_user_message, messages=messages, effective_task_id=effective_task_id, should_review_memory=should_review_memory)
def main(
query: str = None,
query: str | None = None,
model: str = "",
api_key: str = None,
api_key: str | None = None,
base_url: str = "",
max_turns: int = 10,
enabled_toolsets: str = None,
disabled_toolsets: str = None,
enabled_toolsets: str | None = None,
disabled_toolsets: str | None = None,
list_tools: bool = False,
save_trajectories: bool = False,
save_sample: bool = False,

View file

@ -2266,7 +2266,7 @@ def _extract_screenshot_path_from_text(text: str) -> Optional[str]:
def _run_browser_command(
task_id: str,
command: str,
args: List[str] = None,
args: List[str] | None = None,
timeout: Optional[int] = None,
_engine_override: Optional[str] = None,
) -> Dict[str, Any]:

View file

@ -869,7 +869,7 @@ def _build_child_progress_callback(
return kw
def _relay(
event_type: str, tool_name: str = None, preview: str = None, args=None, **kwargs
event_type: str, tool_name: str | None = None, preview: str = None, args=None, **kwargs
):
if not parent_cb:
return

View file

@ -834,7 +834,7 @@ class ShellFileOperations(FileOperations):
self._command_cache: Dict[str, bool] = {}
def _exec(self, command: str, cwd: str = None, timeout: int = None,
stdin_data: str = None) -> ExecuteResult:
stdin_data: str | None = None) -> ExecuteResult:
"""Execute command via terminal backend.
Args:

View file

@ -1653,7 +1653,7 @@ def write_file_tool(path: str, content: str, task_id: str = "default",
def patch_tool(mode: str = "replace", path: str = None, old_string: str = None,
new_string: str = None, replace_all: bool = False, patch: str = None,
new_string: str | None = None, replace_all: bool = False, patch: str = None,
task_id: str = "default", cross_profile: bool = False,
session_id: str | None = None) -> str:
"""Patch a file using replace mode or V4A patch format.
@ -1847,7 +1847,7 @@ def patch_tool(mode: str = "replace", path: str = None, old_string: str = None,
def search_tool(pattern: str, target: str = "content", path: str = ".",
file_glob: str = None, limit: int = 50, offset: int = 0,
file_glob: str | None = None, limit: int = 50, offset: int = 0,
output_mode: str = "content", context: int = 0,
task_id: str = "default") -> str:
"""Search for content or files."""

View file

@ -957,10 +957,10 @@ def _missing_old_text_error(store: "MemoryStore", target: str, action: str) -> s
def memory_tool(
action: str = None,
action: str | None = None,
target: str = "memory",
content: str = None,
old_text: str = None,
content: str | None = None,
old_text: str | None = None,
operations: Optional[List[Dict[str, Any]]] = None,
store: Optional[MemoryStore] = None,
) -> str:

View file

@ -689,10 +689,10 @@ class ProcessRegistry:
def spawn_local(
self,
command: str,
cwd: str = None,
cwd: str | None = None,
task_id: str = "",
session_key: str = "",
env_vars: dict = None,
env_vars: dict | None = None,
use_pty: bool = False,
) -> ProcessSession:
"""
@ -829,7 +829,7 @@ class ProcessRegistry:
self,
env: Any,
command: str,
cwd: str = None,
cwd: str | None = None,
task_id: str = "",
session_key: str = "",
timeout: int = 10,

View file

@ -368,13 +368,13 @@ class ToolRegistry:
toolset: str,
schema: dict,
handler: Callable,
check_fn: Callable = None,
requires_env: list = None,
check_fn: Callable | None = None,
requires_env: list | None = None,
is_async: bool = False,
description: str = "",
emoji: str = "",
max_result_size_chars: int | float | None = None,
dynamic_schema_overrides: Callable = None,
dynamic_schema_overrides: Callable | None = None,
override: bool = False,
):
"""Register a tool. Called at module-import time by each tool file.

View file

@ -305,7 +305,7 @@ def _scroll(
session_id: str,
around_message_id: int,
window: int = 5,
current_session_id: str = None,
current_session_id: str | None = None,
) -> str:
"""Scroll shape: return a window of messages centered on an anchor.
@ -502,7 +502,7 @@ def _discover(
role_filter: Optional[List[str]],
limit: int,
sort: Optional[str],
current_session_id: str = None,
current_session_id: str | None = None,
) -> str:
"""Discovery shape: FTS5 + anchored window + bookends per hit. Single call."""
role_list = role_filter if role_filter else ["user", "assistant"]
@ -618,18 +618,18 @@ def _discover(
def session_search(
query: str = "",
role_filter: str = None,
role_filter: str | None = None,
limit: int = 3,
db=None,
current_session_id: str = None,
current_session_id: str | None = None,
# Scroll shape
session_id: str = None,
around_message_id: int = None,
session_id: str | None = None,
around_message_id: int | None = None,
window: int = 5,
# Discovery shape
sort: str = None,
sort: str | None = None,
# Cross-profile (any shape)
profile: str = None,
profile: str | None = None,
) -> str:
"""Single-shape tool. Mode inferred from which args are set.

View file

@ -918,7 +918,7 @@ def _patch_skill(
name: str,
old_string: str,
new_string: str,
file_path: str = None,
file_path: str | None = None,
replace_all: bool = False,
) -> Dict[str, Any]:
"""Targeted find-and-replace within a skill file.
@ -1323,14 +1323,14 @@ def apply_skill_pending(payload: Dict[str, Any]) -> str:
def skill_manage(
action: str,
name: str,
content: str = None,
category: str = None,
file_path: str = None,
file_content: str = None,
old_string: str = None,
new_string: str = None,
content: str | None = None,
category: str | None = None,
file_path: str | None = None,
file_content: str | None = None,
old_string: str | None = None,
new_string: str | None = None,
replace_all: bool = False,
absorbed_into: str = None,
absorbed_into: str | None = None,
) -> str:
"""
Manage user-created skills. Dispatches to the appropriate action handler.

View file

@ -960,8 +960,8 @@ def _serve_plugin_skill(
def skill_view(
name: str,
file_path: str = None,
task_id: str = None,
file_path: str | None = None,
task_id: str | None = None,
preprocess: bool = True,
) -> str:
"""

View file

@ -1482,10 +1482,10 @@ def _get_modal_backend_state(modal_mode: object | None) -> Dict[str, Any]:
def _create_environment(env_type: str, image: str, cwd: str, timeout: int,
ssh_config: dict = None, container_config: dict = None,
local_config: dict = None,
ssh_config: dict | None = None, container_config: dict = None,
local_config: dict | None = None,
task_id: str = "default",
host_cwd: str = None):
host_cwd: str | None = None):
"""
Create an execution environment for sandboxed command execution.

View file

@ -1355,11 +1355,11 @@ Write only the summary, starting with "[CONTEXT SUMMARY]:" prefix."""
def main(
input: str,
output: str = None,
output: str | None = None,
config: str = "configs/trajectory_compression.yaml",
target_max_tokens: int = None,
tokenizer: str = None,
sample_percent: float = None,
target_max_tokens: int | None = None,
tokenizer: str | None = None,
sample_percent: float | None = None,
seed: int = 42,
dry_run: bool = False,
):